imported from "final" folder
This commit is contained in:
356
.pio/libdeps/esp01_1m/FastLED/examples/AGENTS.md
Normal file
356
.pio/libdeps/esp01_1m/FastLED/examples/AGENTS.md
Normal file
@@ -0,0 +1,356 @@
|
||||
# FastLED Examples Agent Guidelines
|
||||
|
||||
## 🚨 CRITICAL: .INO FILE CREATION RULES
|
||||
|
||||
### ⚠️ THINK BEFORE CREATING .INO FILES ⚠️
|
||||
|
||||
**.ino files should be created SPARINGLY and ONLY when truly justified.**
|
||||
|
||||
### 🚫 WHEN NOT TO CREATE .INO FILES:
|
||||
- **Testing minor code changes** - Use existing test files or unit tests
|
||||
- **Quick API validation** - Use unit tests or modify existing examples
|
||||
- **Debugging specific functions** - Use test files, not new sketches
|
||||
- **One-off experiments** - Create temporary test files instead
|
||||
- **Small feature tests** - Extend existing relevant examples
|
||||
|
||||
### ✅ WHEN TO CREATE .INO FILES:
|
||||
|
||||
#### 1. **Temporary Testing (.ino)**
|
||||
**Use Pattern:** `temp_<feature>.ino` or `test_<api>.ino`
|
||||
```cpp
|
||||
// temp_json_api.ino - Testing new JSON fetch functionality
|
||||
// test_networking.ino - Validating network stack changes
|
||||
```
|
||||
- ✅ **FOR:** Testing new APIs during development
|
||||
- ✅ **FOR:** Quick prototyping and validation
|
||||
- ✅ **DELETE AFTER USE** - These are temporary by design
|
||||
|
||||
#### 2. **Significant New Feature Examples**
|
||||
**Use Pattern:** `examples/<FeatureName>/<FeatureName>.ino`
|
||||
```cpp
|
||||
// examples/JsonFetchApi/JsonFetchApi.ino - Comprehensive JSON API example
|
||||
// examples/NetworkStack/NetworkStack.ino - Major networking features
|
||||
```
|
||||
- ✅ **FOR:** Large, comprehensive new features
|
||||
- ✅ **FOR:** APIs that warrant dedicated examples
|
||||
- ✅ **FOR:** Features that users will commonly implement
|
||||
- ✅ **PERMANENT** - These become part of the example library
|
||||
|
||||
### 📋 CREATION CHECKLIST:
|
||||
|
||||
**Before creating ANY .ino file, ask:**
|
||||
|
||||
1. **🤔 Is this testing a new API?**
|
||||
- YES → Create `temp_<name>.ino`, delete after testing
|
||||
- NO → Consider alternatives
|
||||
|
||||
2. **🤔 Is this a significant new feature that users will commonly use?**
|
||||
- YES → Create `examples/<FeatureName>/<FeatureName>.ino`
|
||||
- NO → Use existing examples or test files
|
||||
|
||||
3. **🤔 Can I modify an existing example instead?**
|
||||
- YES → Extend existing example rather than creating new
|
||||
- NO → Proceed with creation
|
||||
|
||||
4. **🤔 Is this just for debugging/validation?**
|
||||
- YES → Use unit tests or temporary test files
|
||||
- NO → Consider if it meets the "significant feature" criteria
|
||||
|
||||
### 🔍 REVIEW CRITERIA:
|
||||
|
||||
**For Feature Examples (.ino files that stay):**
|
||||
- ✅ **Demonstrates complete, real-world usage patterns**
|
||||
- ✅ **Covers multiple aspects of the feature comprehensively**
|
||||
- ✅ **Provides educational value for users**
|
||||
- ✅ **Shows best practices and common use cases**
|
||||
- ✅ **Is likely to be referenced by multiple users**
|
||||
|
||||
**For Temporary Testing (.ino files that get deleted):**
|
||||
- ✅ **Clearly named as temporary (temp_*, test_*)**
|
||||
- ✅ **Focused on specific API validation**
|
||||
- ✅ **Will be deleted after development cycle**
|
||||
- ✅ **Too complex for unit test framework**
|
||||
|
||||
### ❌ EXAMPLES OF WHAT NOT TO CREATE:
|
||||
- `test_basic_led.ino` - Use existing Blink example
|
||||
- `debug_colors.ino` - Use existing ColorPalette example
|
||||
- `quick_brightness.ino` - Use unit tests or modify existing example
|
||||
- `validate_pins.ino` - Use PinTest example or unit tests
|
||||
|
||||
### ✅ EXAMPLES OF JUSTIFIED CREATIONS:
|
||||
- `temp_new_wifi_api.ino` - Testing major new WiFi functionality (temporary)
|
||||
- `examples/MachineLearning/MachineLearning.ino` - New ML integration feature (permanent)
|
||||
- `temp_performance_test.ino` - Validating optimization changes (temporary)
|
||||
|
||||
### 🧹 CLEANUP RESPONSIBILITY:
|
||||
- **Temporary files:** Creator must delete when testing is complete
|
||||
- **Feature examples:** Must be maintained and updated as API evolves
|
||||
- **Abandoned files:** Regular cleanup reviews to remove unused examples
|
||||
|
||||
**Remember: The examples directory is user-facing documentation. Every .ino file should provide clear value to FastLED users.**
|
||||
|
||||
## Code Standards for Examples
|
||||
|
||||
### Use fl:: Namespace (Not std::)
|
||||
**DO NOT use `std::` prefixed functions or headers in examples.** This project provides its own STL-equivalent implementations under the `fl::` namespace.
|
||||
|
||||
**Examples of what to avoid and use instead:**
|
||||
|
||||
**Headers:**
|
||||
- ❌ `#include <vector>` → ✅ `#include "fl/vector.h"`
|
||||
- ❌ `#include <string>` → ✅ `#include "fl/string.h"`
|
||||
- ❌ `#include <optional>` → ✅ `#include "fl/optional.h"`
|
||||
- ❌ `#include <memory>` → ✅ `#include "fl/scoped_ptr.h"` or `#include "fl/ptr.h"`
|
||||
|
||||
**Functions and classes:**
|
||||
- ❌ `std::move()` → ✅ `fl::move()`
|
||||
- ❌ `std::vector` → ✅ `fl::vector`
|
||||
- ❌ `std::string` → ✅ `fl::string`
|
||||
|
||||
**Why:** The project maintains its own implementations to ensure compatibility across all supported platforms and to avoid bloating the library with unnecessary STL dependencies.
|
||||
|
||||
### Memory Management
|
||||
**🚨 CRITICAL: Always use proper RAII patterns - smart pointers, moveable objects, or wrapper classes instead of raw pointers for resource management.**
|
||||
|
||||
**Resource Management Options:**
|
||||
- ✅ **PREFERRED**: `fl::shared_ptr<T>` for shared ownership (multiple references to same object)
|
||||
- ✅ **PREFERRED**: `fl::unique_ptr<T>` for exclusive ownership (single owner, automatic cleanup)
|
||||
- ✅ **PREFERRED**: Moveable wrapper objects (like `fl::promise<T>`) for safe copying and transferring of unique resources
|
||||
- ✅ **ACCEPTABLE**: `fl::vector<T>` storing objects by value when objects support move/copy semantics
|
||||
- ❌ **AVOID**: `fl::vector<T*>` storing raw pointers - use `fl::vector<fl::shared_ptr<T>>` or `fl::vector<fl::unique_ptr<T>>`
|
||||
- ❌ **AVOID**: Manual `new`/`delete` - use `fl::make_shared<T>()` or `fl::make_unique<T>()`
|
||||
|
||||
**Examples:**
|
||||
```cpp
|
||||
// ✅ GOOD - Using smart pointers
|
||||
fl::vector<fl::shared_ptr<HttpClient>> mActiveClients;
|
||||
auto client = fl::make_shared<HttpClient>();
|
||||
mActiveClients.push_back(client);
|
||||
|
||||
// ✅ GOOD - Using unique_ptr for exclusive ownership
|
||||
fl::unique_ptr<HttpClient> client = fl::make_unique<HttpClient>();
|
||||
|
||||
// ✅ GOOD - Moveable wrapper objects (fl::promise example)
|
||||
fl::vector<fl::promise<Response>> mActivePromises; // Copyable wrapper around unique future
|
||||
fl::promise<Response> promise = fetch.get(url).execute();
|
||||
mActivePromises.push_back(promise); // Safe copy, shared internal state
|
||||
|
||||
// ❌ BAD - Raw pointers require manual memory management
|
||||
fl::vector<Promise*> mActivePromises; // Memory leaks possible
|
||||
Promise* promise = new Promise(); // Who calls delete?
|
||||
```
|
||||
|
||||
### Debug Printing
|
||||
**Use `FL_WARN` for debug printing in examples.** This ensures consistent debug output that works in both unit tests and live application testing.
|
||||
|
||||
**Usage:**
|
||||
- ✅ `FL_WARN("Debug message: " << message);`
|
||||
- ❌ `FL_WARN("Value: %d", value);`
|
||||
|
||||
### No Emoticons or Emojis
|
||||
**NO emoticons or emoji characters are allowed in C++ source files.** This ensures professional, maintainable code that works correctly across all platforms and development environments.
|
||||
|
||||
**Examples of what NOT to do in .ino files:**
|
||||
```cpp
|
||||
// ❌ BAD - Emoticons in comments
|
||||
// 🎯 This function handles user input
|
||||
|
||||
// ❌ BAD - Emoticons in log messages
|
||||
FL_WARN("✅ Operation successful!");
|
||||
FL_WARN("❌ Error occurred: " << error_msg);
|
||||
|
||||
// ❌ BAD - Emoticons in string literals
|
||||
const char* status = "🔄 Processing...";
|
||||
```
|
||||
|
||||
**Examples of correct alternatives:**
|
||||
```cpp
|
||||
// ✅ GOOD - Clear text in comments
|
||||
// TUTORIAL: This function handles user input
|
||||
|
||||
// ✅ GOOD - Text prefixes in log messages
|
||||
FL_WARN("SUCCESS: Operation completed successfully!");
|
||||
FL_WARN("ERROR: Failed to process request: " << error_msg);
|
||||
|
||||
// ✅ GOOD - Descriptive text in string literals
|
||||
const char* status = "PROCESSING: Request in progress...";
|
||||
```
|
||||
|
||||
### JSON Usage - Ideal API Patterns
|
||||
**🎯 PREFERRED: Use the modern `fl::Json` class for all JSON operations.** FastLED provides an ideal JSON API that prioritizes type safety, ergonomics, and crash-proof operation.
|
||||
|
||||
**✅ IDIOMATIC JSON USAGE:**
|
||||
```cpp
|
||||
// NEW: Clean, safe, idiomatic API
|
||||
fl::Json json = fl::Json::parse(jsonStr);
|
||||
int brightness = json["config"]["brightness"] | 128; // Gets value or 128 default
|
||||
string name = json["device"]["name"] | string("default"); // Type-safe with default
|
||||
bool enabled = json["features"]["networking"] | false; // Never crashes
|
||||
|
||||
// Array operations
|
||||
if (json["effects"].contains("rainbow")) {
|
||||
// Safe array checking
|
||||
}
|
||||
```
|
||||
|
||||
**❌ DISCOURAGED: Verbose legacy API:**
|
||||
```cpp
|
||||
// OLD: Verbose, error-prone API (still works, but not recommended)
|
||||
fl::JsonDocument doc;
|
||||
fl::string error;
|
||||
fl::parseJson(jsonStr, &doc, &error);
|
||||
int brightness = doc["config"]["brightness"].as<int>(); // Can crash if missing
|
||||
```
|
||||
|
||||
**📚 Reference Example:** See `examples/Json/Json.ino` for comprehensive usage patterns and API comparison.
|
||||
|
||||
## Example Compilation Commands
|
||||
|
||||
### Platform Compilation
|
||||
```bash
|
||||
# Compile examples for specific platforms
|
||||
uv run ci/ci-compile.py uno --examples Blink
|
||||
uv run ci/ci-compile.py esp32dev --examples Blink
|
||||
uv run ci/ci-compile.py teensy31 --examples Blink
|
||||
bash compile uno --examples Blink
|
||||
```
|
||||
|
||||
### WASM Compilation
|
||||
**🎯 HOW TO COMPILE ANY ARDUINO SKETCH TO WASM:**
|
||||
|
||||
**Basic Compilation Commands:**
|
||||
```bash
|
||||
# Compile any sketch directory to WASM
|
||||
uv run ci/wasm_compile.py path/to/your/sketch
|
||||
|
||||
# Quick compile test (compile only, no browser)
|
||||
uv run ci/wasm_compile.py path/to/your/sketch --just-compile
|
||||
|
||||
# Compile examples/Blink to WASM
|
||||
uv run ci/wasm_compile.py examples/Blink --just-compile
|
||||
|
||||
# Compile examples/NetTest to WASM (test fetch API)
|
||||
uv run ci/wasm_compile.py examples/NetTest --just-compile
|
||||
|
||||
# Compile examples/DemoReel100 to WASM
|
||||
uv run ci/wasm_compile.py examples/DemoReel100 --just-compile
|
||||
```
|
||||
|
||||
**Output:** Creates `fastled_js/` folder with:
|
||||
- `fastled.js` - JavaScript loader
|
||||
- `fastled.wasm` - WebAssembly binary
|
||||
- `index.html` - HTML page to run the sketch
|
||||
|
||||
**Run in Browser:**
|
||||
```bash
|
||||
# Simple HTTP server to test
|
||||
cd fastled_js
|
||||
python -m http.server 8000
|
||||
# Open http://localhost:8000
|
||||
```
|
||||
|
||||
**🚨 REQUIREMENTS:**
|
||||
- **Docker must be installed and running**
|
||||
- **Internet connection** (for Docker image download on first run)
|
||||
- **~1GB RAM** for Docker container during compilation
|
||||
|
||||
### WASM Testing Requirements
|
||||
|
||||
**🚨 MANDATORY: Always test WASM compilation after platform file changes**
|
||||
|
||||
**Platform Testing Commands:**
|
||||
```bash
|
||||
# Test WASM platform changes (for platform developers)
|
||||
uv run ci/wasm_compile.py examples/wasm --just-compile
|
||||
|
||||
# Quick compile test for any sketch (compile only, no browser)
|
||||
uv run ci/wasm_compile.py examples/Blink --just-compile
|
||||
|
||||
# Quick compile test for NetTest example
|
||||
uv run ci/wasm_compile.py examples/NetTest --just-compile
|
||||
|
||||
# Quick test without full build
|
||||
uv run ci/wasm_compile.py examples/wasm --quick
|
||||
```
|
||||
|
||||
**Watch For These Error Patterns:**
|
||||
- `error: conflicting types for 'function_name'`
|
||||
- `error: redefinition of 'function_name'`
|
||||
- `warning: attribute declaration must precede definition`
|
||||
- `RuntimeError: unreachable` (often async-related)
|
||||
|
||||
**MANDATORY RULES:**
|
||||
- **ALWAYS test WASM compilation** after modifying any WASM platform files
|
||||
- **USE `uv run ci/wasm_compile.py` for validation**
|
||||
- **WATCH for unified build conflicts** in compilation output
|
||||
- **VERIFY async operations work properly** in browser environment
|
||||
|
||||
## Compiler Warning Suppression
|
||||
|
||||
**ALWAYS use the FastLED compiler control macros from `fl/compiler_control.h` for warning suppression.** This ensures consistent cross-compiler support and proper handling of platform differences.
|
||||
|
||||
**Correct Warning Suppression Pattern:**
|
||||
```cpp
|
||||
#include "fl/compiler_control.h"
|
||||
|
||||
// Suppress specific warning around problematic code
|
||||
FL_DISABLE_WARNING_PUSH
|
||||
FL_DISABLE_FORMAT_TRUNCATION // Use specific warning macros
|
||||
// ... code that triggers warnings ...
|
||||
FL_DISABLE_WARNING_POP
|
||||
```
|
||||
|
||||
**Available Warning Suppression Macros:**
|
||||
- ✅ `FL_DISABLE_WARNING_PUSH` / `FL_DISABLE_WARNING_POP` - Standard push/pop pattern
|
||||
- ✅ `FL_DISABLE_WARNING(warning_name)` - Generic warning suppression (use sparingly)
|
||||
- ✅ `FL_DISABLE_WARNING_GLOBAL_CONSTRUCTORS` - Clang global constructor warnings
|
||||
- ✅ `FL_DISABLE_WARNING_SELF_ASSIGN_OVERLOADED` - Clang self-assignment warnings
|
||||
- ✅ `FL_DISABLE_FORMAT_TRUNCATION` - GCC format truncation warnings
|
||||
|
||||
**What NOT to do:**
|
||||
- ❌ **NEVER use raw `#pragma` directives** - they don't handle compiler differences
|
||||
- ❌ **NEVER write manual `#ifdef __clang__` / `#ifdef __GNUC__` blocks** - use the macros
|
||||
- ❌ **NEVER ignore warnings without suppression** - fix the issue or suppress appropriately
|
||||
|
||||
## Exception Handling
|
||||
|
||||
**DO NOT use try-catch blocks or C++ exception handling in examples.** FastLED is designed to work on embedded systems like Arduino where exception handling may not be available or desired due to memory and performance constraints.
|
||||
|
||||
**Use Error Handling Alternatives:**
|
||||
- ✅ **Return error codes:** `bool function() { return false; }` or custom error enums
|
||||
- ✅ **Optional types:** `fl::optional<T>` for functions that may not return a value
|
||||
- ✅ **Assertions:** `FL_ASSERT(condition)` for debug-time validation
|
||||
- ✅ **Early returns:** `if (!valid) return false;` for error conditions
|
||||
- ✅ **Status objects:** Custom result types that combine success/failure with data
|
||||
|
||||
**Examples of proper error handling:**
|
||||
```cpp
|
||||
// Good: Using return codes
|
||||
bool initializeHardware() {
|
||||
if (!setupPins()) {
|
||||
FL_WARN("Failed to setup pins");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Good: Using fl::optional
|
||||
fl::optional<float> calculateValue(int input) {
|
||||
if (input < 0) {
|
||||
return fl::nullopt; // No value, indicates error
|
||||
}
|
||||
return fl::make_optional(sqrt(input));
|
||||
}
|
||||
|
||||
// Good: Using early returns
|
||||
void processData(const uint8_t* data, size_t len) {
|
||||
if (!data || len == 0) {
|
||||
FL_WARN("Invalid input data");
|
||||
return; // Early return on error
|
||||
}
|
||||
// Process data...
|
||||
}
|
||||
```
|
||||
|
||||
## Memory Refresh Rule
|
||||
**🚨 ALL AGENTS: Read examples/AGENTS.md before concluding example work to refresh memory about .ino file creation rules and example coding standards.**
|
||||
@@ -0,0 +1,27 @@
|
||||
// Simple Adafruit Bridge Demo
|
||||
//
|
||||
// This example shows how to use the Adafruit_NeoPixel library with FastLED.
|
||||
// As long as the Adafruit_NeoPixel library is installed (that is #include <Adafruit_NeoPixel.h>
|
||||
// is present), this example will work. Otherwise you'll get warnings about a missing driver.
|
||||
|
||||
#define FASTLED_USE_ADAFRUIT_NEOPIXEL
|
||||
#include "FastLED.h"
|
||||
|
||||
#define DATA_PIN 3
|
||||
#define NUM_LEDS 10
|
||||
|
||||
CRGB leds[NUM_LEDS];
|
||||
uint8_t hue = 0;
|
||||
|
||||
void setup() {
|
||||
FastLED.addLeds<WS2812, DATA_PIN, GRB>(leds, NUM_LEDS);
|
||||
FastLED.setBrightness(50);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
fill_rainbow(leds, NUM_LEDS, hue, 255/NUM_LEDS);
|
||||
FastLED.show();
|
||||
|
||||
hue++;
|
||||
delay(50);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/// @file AnalogOutput.ino
|
||||
/// @brief Demonstrates how to use FastLED color functions even without a "pixel-addressible" smart LED strip.
|
||||
/// @example AnalogOutput.ino
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <FastLED.h>
|
||||
#include "./compat.h"
|
||||
|
||||
|
||||
|
||||
// Example showing how to use FastLED color functions
|
||||
// even when you're NOT using a "pixel-addressible" smart LED strip.
|
||||
//
|
||||
// This example is designed to control an "analog" RGB LED strip
|
||||
// (or a single RGB LED) being driven by Arduino PWM output pins.
|
||||
// So this code never calls FastLED.addLEDs() or FastLED.show().
|
||||
//
|
||||
// This example illustrates one way you can use just the portions
|
||||
// of FastLED that you need. In this case, this code uses just the
|
||||
// fast HSV color conversion code.
|
||||
//
|
||||
// In this example, the RGB values are output on three separate
|
||||
// 'analog' PWM pins, one for red, one for green, and one for blue.
|
||||
|
||||
#define REDPIN 5
|
||||
#define GREENPIN 6
|
||||
#define BLUEPIN 3
|
||||
|
||||
// showAnalogRGB: this is like FastLED.show(), but outputs on
|
||||
// analog PWM output pins instead of sending data to an intelligent,
|
||||
// pixel-addressable LED strip.
|
||||
//
|
||||
// This function takes the incoming RGB values and outputs the values
|
||||
// on three analog PWM output pins to the r, g, and b values respectively.
|
||||
void showAnalogRGB( const CRGB& rgb)
|
||||
{
|
||||
analogWrite(REDPIN, rgb.r );
|
||||
analogWrite(GREENPIN, rgb.g );
|
||||
analogWrite(BLUEPIN, rgb.b );
|
||||
}
|
||||
|
||||
|
||||
|
||||
// colorBars: flashes Red, then Green, then Blue, then Black.
|
||||
// Helpful for diagnosing if you've mis-wired which is which.
|
||||
void colorBars()
|
||||
{
|
||||
showAnalogRGB( CRGB::Red ); delay(500);
|
||||
showAnalogRGB( CRGB::Green ); delay(500);
|
||||
showAnalogRGB( CRGB::Blue ); delay(500);
|
||||
showAnalogRGB( CRGB::Black ); delay(500);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
static uint8_t hue;
|
||||
hue = hue + 1;
|
||||
// Use FastLED automatic HSV->RGB conversion
|
||||
showAnalogRGB( CHSV( hue, 255, 255) );
|
||||
|
||||
delay(20);
|
||||
}
|
||||
|
||||
|
||||
void setup() {
|
||||
pinMode(REDPIN, OUTPUT);
|
||||
pinMode(GREENPIN, OUTPUT);
|
||||
pinMode(BLUEPIN, OUTPUT);
|
||||
|
||||
// Flash the "hello" color sequence: R, G, B, black.
|
||||
colorBars();
|
||||
}
|
||||
42
.pio/libdeps/esp01_1m/FastLED/examples/AnalogOutput/compat.h
Normal file
42
.pio/libdeps/esp01_1m/FastLED/examples/AnalogOutput/compat.h
Normal file
@@ -0,0 +1,42 @@
|
||||
|
||||
|
||||
#ifdef ARDUINO_ESP32_DEV
|
||||
#include "fl/compiler_control.h"
|
||||
|
||||
#include "platforms/esp/esp_version.h"
|
||||
#include "driver/ledc.h"
|
||||
#include "esp32-hal-ledc.h"
|
||||
|
||||
// Ancient versions of ESP32 on Arduino (IDF < 4.0) did not have analogWrite() defined.
|
||||
// IDF 4.4 and 5.0+ both have analogWrite() available, so this polyfill is only needed
|
||||
// for very old IDF versions. We use a weak symbol so it auto-disables on newer platforms.
|
||||
#if !ESP_IDF_VERSION_4_OR_HIGHER
|
||||
FL_LINK_WEAK void analogWrite(uint8_t pin, int value) {
|
||||
// Setup PWM channel for the pin if not already done
|
||||
static bool channels_setup[16] = {false}; // ESP32 has 16 PWM channels
|
||||
static uint8_t channel_counter = 0;
|
||||
|
||||
// Find or assign channel for this pin
|
||||
static uint8_t pin_to_channel[40] = {255}; // ESP32 has up to 40 GPIO pins, 255 = unassigned
|
||||
if (pin_to_channel[pin] == 255) {
|
||||
pin_to_channel[pin] = channel_counter++;
|
||||
if (channel_counter > 15) channel_counter = 0; // Wrap around
|
||||
}
|
||||
|
||||
uint8_t channel = pin_to_channel[pin];
|
||||
|
||||
// Setup channel if not already done
|
||||
if (!channels_setup[channel]) {
|
||||
ledcSetup(channel, 5000, 8); // 5kHz frequency, 8-bit resolution
|
||||
ledcAttachPin(pin, channel);
|
||||
channels_setup[channel] = true;
|
||||
}
|
||||
|
||||
// Write PWM value (0-255 for 8-bit resolution)
|
||||
ledcWrite(channel, value);
|
||||
}
|
||||
#endif // !ESP_IDF_VERSION_4_OR_HIGHER
|
||||
|
||||
|
||||
|
||||
#endif // ESP32
|
||||
158
.pio/libdeps/esp01_1m/FastLED/examples/Animartrix/Animartrix.ino
Normal file
158
.pio/libdeps/esp01_1m/FastLED/examples/Animartrix/Animartrix.ino
Normal file
@@ -0,0 +1,158 @@
|
||||
/// @file Animartrix.ino
|
||||
/// @brief Demo of the Animatrix effects
|
||||
/// @example Animartrix.ino
|
||||
///
|
||||
/// 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`
|
||||
/// 4. When the compiler is done a web page will open.
|
||||
///
|
||||
/// @author Stefan Petrick
|
||||
/// @author Zach Vorhies (FastLED adaptation)
|
||||
///
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#define FL_ANIMARTRIX_USES_FAST_MATH 1
|
||||
|
||||
/*
|
||||
Performence notes @64x64:
|
||||
* ESP32-S3:
|
||||
* FL_ANIMARTRIX_USES_FAST_MATH 0: 143ms
|
||||
* FL_ANIMARTRIX_USES_FAST_MATH 1: 90ms
|
||||
*/
|
||||
|
||||
#include "FastLED.h"
|
||||
|
||||
#if !SKETCH_HAS_LOTS_OF_MEMORY
|
||||
// Platform does not have enough memory
|
||||
void setup() {}
|
||||
void loop() {}
|
||||
#else
|
||||
|
||||
|
||||
// DRAW TIME: 7ms
|
||||
|
||||
|
||||
#include <FastLED.h>
|
||||
#include "fl/json.h"
|
||||
#include "fl/slice.h"
|
||||
#include "fx/fx_engine.h"
|
||||
|
||||
#include "fx/2d/animartrix.hpp"
|
||||
#include "fl/ui.h"
|
||||
|
||||
using namespace fl;
|
||||
|
||||
|
||||
#define LED_PIN 3
|
||||
#define BRIGHTNESS 32
|
||||
#define COLOR_ORDER GRB
|
||||
|
||||
#define MATRIX_WIDTH 64
|
||||
#define MATRIX_HEIGHT 64
|
||||
|
||||
#define NUM_LEDS (MATRIX_WIDTH * MATRIX_HEIGHT)
|
||||
|
||||
#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
|
||||
|
||||
|
||||
#define POWER_LIMITER_ACTIVE
|
||||
#define POWER_VOLTS 5
|
||||
#define POWER_MILLIAMPS 2000
|
||||
|
||||
|
||||
CRGB leds[NUM_LEDS];
|
||||
XYMap xyMap = XYMap::constructRectangularGrid(MATRIX_WIDTH, MATRIX_HEIGHT);
|
||||
|
||||
|
||||
UITitle title("Animartrix");
|
||||
UIDescription description("Demo of the Animatrix effects. @author of fx is StefanPetrick");
|
||||
|
||||
UISlider brightness("Brightness", BRIGHTNESS, 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);
|
||||
|
||||
const bool kPowerLimiterActive = false;
|
||||
|
||||
void setup_max_power() {
|
||||
if (kPowerLimiterActive) {
|
||||
FastLED.setMaxPowerInVoltsAndMilliamps(POWER_VOLTS, POWER_MILLIAMPS); // Set max power to 2 amps
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
FL_WARN("*** SETUP ***");
|
||||
|
||||
auto screen_map = xyMap.toScreenMap();
|
||||
screen_map.setDiameter(LED_DIAMETER);
|
||||
FastLED.addLeds<WS2811, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS)
|
||||
.setCorrection(TypicalLEDStrip)
|
||||
.setScreenMap(screen_map);
|
||||
FastLED.setBrightness(brightness);
|
||||
setup_max_power();
|
||||
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() {
|
||||
FL_WARN("*** LOOP ***");
|
||||
uint32_t start = millis();
|
||||
FastLED.setBrightness(brightness);
|
||||
fxEngine.setSpeed(timeSpeed);
|
||||
static int lastFxIndex = -1;
|
||||
if (fxIndex.value() != lastFxIndex) {
|
||||
lastFxIndex = fxIndex;
|
||||
animartrix.fxSet(fxIndex);
|
||||
}
|
||||
fxEngine.draw(millis(), leds);
|
||||
uint32_t end = millis();
|
||||
FL_WARN("*** DRAW TIME: " << int(end - start) << "ms");
|
||||
FastLED.show();
|
||||
uint32_t end2 = millis();
|
||||
FL_WARN("*** SHOW + DRAW TIME: " << int(end2 - start) << "ms");
|
||||
}
|
||||
|
||||
|
||||
#endif // __AVR__
|
||||
38
.pio/libdeps/esp01_1m/FastLED/examples/Apa102/Apa102.ino
Normal file
38
.pio/libdeps/esp01_1m/FastLED/examples/Apa102/Apa102.ino
Normal file
@@ -0,0 +1,38 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <FastLED.h>
|
||||
#include <lib8tion.h>
|
||||
|
||||
#define NUM_LEDS 20
|
||||
|
||||
#define STRIP_DATA_PIN 1
|
||||
#define STRIP_CLOCK_PIN 2
|
||||
|
||||
CRGB leds[NUM_LEDS] = {0}; // Software gamma mode.
|
||||
|
||||
void setup() {
|
||||
delay(500); // power-up safety delay
|
||||
// Two strips of LEDs, one in HD mode, one in software gamma mode.
|
||||
FastLED.addLeds<APA102, STRIP_DATA_PIN, STRIP_CLOCK_PIN, RGB>(leds, NUM_LEDS);
|
||||
}
|
||||
|
||||
uint8_t wrap_8bit(int i) {
|
||||
// Modulo % operator here wraps a large "i" so that it is
|
||||
// always in [0, 255] range when returned. For example, if
|
||||
// "i" is 256, then this will return 0. If "i" is 257,
|
||||
// then this will return 1. No matter how big the "i" is, the
|
||||
// output range will always be [0, 255]
|
||||
return i % 256;
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Draw a linear ramp of brightnesses to showcase the difference between
|
||||
// the HD and non-HD mode.
|
||||
for (int i = 0; i < NUM_LEDS; i++) {
|
||||
uint8_t brightness = map(i, 0, NUM_LEDS - 1, 0, 255);
|
||||
CRGB c(brightness, brightness, brightness); // Just make a shade of white.
|
||||
leds[i] = c;
|
||||
}
|
||||
FastLED.show(); // All LEDs are now displayed.
|
||||
delay(8); // Wait 8 milliseconds until the next frame.
|
||||
}
|
||||
88
.pio/libdeps/esp01_1m/FastLED/examples/Apa102HD/Apa102HD.ino
Normal file
88
.pio/libdeps/esp01_1m/FastLED/examples/Apa102HD/Apa102HD.ino
Normal file
@@ -0,0 +1,88 @@
|
||||
/// @file Apa102HD.ino
|
||||
/// @brief Example showing how to use the APA102HD gamma correction.
|
||||
///
|
||||
/// In this example we compare two strips of LEDs.
|
||||
/// One strip is in HD mode, the other is in software gamma mode.
|
||||
///
|
||||
/// Each strip is a linear ramp of brightnesses, from 0 to 255.
|
||||
/// Showcasing all the different brightnesses.
|
||||
///
|
||||
/// Why do we love gamma correction? Gamma correction more closely
|
||||
/// matches how humans see light. Led values are measured in fractions
|
||||
/// of max power output (1/255, 2/255, etc.), while humans see light
|
||||
/// in a logarithmic way. Gamma correction converts to this eye friendly
|
||||
/// curve. Gamma correction wants a LED with a high bit depth. The APA102
|
||||
/// gives us the standard 3 components (red, green, blue) with 8 bits each, it
|
||||
/// *also* has a 5 bit brightness component. This gives us a total of 13 bits,
|
||||
/// which allows us to achieve a higher dynamic range. This means deeper fades.
|
||||
///
|
||||
/// Example:
|
||||
/// CRGB leds[NUM_LEDS] = {0};
|
||||
/// void setup() {
|
||||
/// FastLED.addLeds<
|
||||
/// APA102HD, // <--- This selects HD mode.
|
||||
/// STRIP_0_DATA_PIN,
|
||||
/// STRIP_0_CLOCK_PIN,
|
||||
/// RGB
|
||||
/// >(leds, NUM_LEDS);
|
||||
/// }
|
||||
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <FastLED.h>
|
||||
#include <lib8tion.h>
|
||||
|
||||
#define NUM_LEDS 20
|
||||
// uint8_t DATA_PIN, uint8_t CLOCK_PIN,
|
||||
|
||||
#define STRIP_0_DATA_PIN 1
|
||||
#define STRIP_0_CLOCK_PIN 2
|
||||
#define STRIP_1_DATA_PIN 3
|
||||
#define STRIP_1_CLOCK_PIN 4
|
||||
|
||||
|
||||
CRGB leds_hd[NUM_LEDS] = {0}; // HD mode implies gamma.
|
||||
CRGB leds[NUM_LEDS] = {0}; // Software gamma mode.
|
||||
|
||||
// This is the regular gamma correction function that we used to have
|
||||
// to do. It's used here to showcase the difference between APA102HD
|
||||
// mode which does the gamma correction for you.
|
||||
CRGB software_gamma(const CRGB& in) {
|
||||
CRGB out;
|
||||
// dim8_raw are the old gamma correction functions.
|
||||
out.r = dim8_raw(in.r);
|
||||
out.g = dim8_raw(in.g);
|
||||
out.b = dim8_raw(in.b);
|
||||
return out;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
delay(500); // power-up safety delay
|
||||
// Two strips of LEDs, one in HD mode, one in software gamma mode.
|
||||
FastLED.addLeds<APA102HD, STRIP_0_DATA_PIN, STRIP_0_CLOCK_PIN, RGB>(leds_hd, NUM_LEDS);
|
||||
FastLED.addLeds<APA102, STRIP_1_DATA_PIN, STRIP_1_CLOCK_PIN, RGB>(leds, NUM_LEDS);
|
||||
}
|
||||
|
||||
uint8_t wrap_8bit(int i) {
|
||||
// Module % operator here wraps a large "i" so that it is
|
||||
// always in [0, 255] range when returned. For example, if
|
||||
// "i" is 256, then this will return 0. If "i" is 257
|
||||
// then this will return 1. No matter how big the "i" is, the
|
||||
// output range will always be [0, 255]
|
||||
return i % 256;
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Draw a a linear ramp of brightnesses to showcase the difference between
|
||||
// the HD and non-HD mode.
|
||||
for (int i = 0; i < NUM_LEDS; i++) {
|
||||
uint8_t brightness = map(i, 0, NUM_LEDS - 1, 0, 255);
|
||||
CRGB c(brightness, brightness, brightness); // Just make a shade of white.
|
||||
leds_hd[i] = c; // The APA102HD leds do their own gamma correction.
|
||||
CRGB c_gamma_corrected = software_gamma(c);
|
||||
leds[i] = c_gamma_corrected; // Set the software gamma corrected
|
||||
// values to the other strip.
|
||||
}
|
||||
FastLED.show(); // All leds are now written out.
|
||||
delay(8); // Wait 8 milliseconds until the next frame.
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/// @file Apa102HD.ino
|
||||
/// @brief Example showing how to use the APA102HD gamma correction with user override.
|
||||
|
||||
#define FASTLED_FIVE_BIT_HD_BITSHIFT_FUNCTION_OVERRIDE
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <FastLED.h>
|
||||
#include <lib8tion.h>
|
||||
|
||||
#define NUM_LEDS 20
|
||||
// uint8_t DATA_PIN, uint8_t CLOCK_PIN,
|
||||
|
||||
#define STRIP_0_DATA_PIN 1
|
||||
#define STRIP_0_CLOCK_PIN 2
|
||||
#define STRIP_1_DATA_PIN 3
|
||||
#define STRIP_1_CLOCK_PIN 4
|
||||
|
||||
void fl::five_bit_hd_gamma_bitshift(CRGB colors,
|
||||
CRGB scale,
|
||||
uint8_t global_brightness,
|
||||
CRGB* out_colors,
|
||||
uint8_t *out_power_5bit) {
|
||||
// all 0 values for output
|
||||
*out_colors = CRGB(0, 0, 0);
|
||||
*out_power_5bit = 0;
|
||||
Serial.println("Override function called");
|
||||
}
|
||||
|
||||
CRGB leds_hd[NUM_LEDS] = {0}; // HD mode implies gamma.
|
||||
|
||||
|
||||
void setup() {
|
||||
delay(500); // power-up safety delay
|
||||
// Two strips of LEDs, one in HD mode, one in software gamma mode.
|
||||
FastLED.addLeds<APA102HD, STRIP_0_DATA_PIN, STRIP_0_CLOCK_PIN, RGB>(
|
||||
leds_hd, NUM_LEDS);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Draw a a linear ramp of brightnesses to showcase the difference between
|
||||
// the HD and non-HD mode.
|
||||
for (int i = 0; i < NUM_LEDS; i++) {
|
||||
uint8_t brightness = map(i, 0, NUM_LEDS - 1, 0, 255);
|
||||
CRGB c(brightness, brightness,
|
||||
brightness); // Just make a shade of white.
|
||||
leds_hd[i] = c; // The APA102HD leds do their own gamma correction.
|
||||
}
|
||||
FastLED.show(); // All leds are now written out.
|
||||
delay(8); // Wait 8 milliseconds until the next frame.
|
||||
}
|
||||
35
.pio/libdeps/esp01_1m/FastLED/examples/Async/Async.h
Normal file
35
.pio/libdeps/esp01_1m/FastLED/examples/Async/Async.h
Normal file
@@ -0,0 +1,35 @@
|
||||
// @file examples/TaskExample/TaskExample.ino
|
||||
// @brief Example showing how to use the fl::task API
|
||||
|
||||
#include "FastLED.h"
|
||||
#include "fl/task.h"
|
||||
#include "fl/async.h"
|
||||
|
||||
#define NUM_LEDS 60
|
||||
#define DATA_PIN 5
|
||||
|
||||
CRGB leds[NUM_LEDS];
|
||||
CRGB current_color = CRGB::Black;
|
||||
|
||||
void setup() {
|
||||
FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
|
||||
FastLED.setBrightness(16);
|
||||
|
||||
// Create a task that runs every 100ms to change color
|
||||
auto colorPicker = fl::task::every_ms(100, FL_TRACE)
|
||||
.then([] {
|
||||
current_color = CHSV(random8(), 255, 255);
|
||||
});
|
||||
|
||||
// Create a task that runs at 60fps to update the LEDs
|
||||
auto displayTask = fl::task::at_framerate(60, FL_TRACE)
|
||||
.then([] {
|
||||
fill_solid(leds, NUM_LEDS, current_color);
|
||||
FastLED.show();
|
||||
});
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Yield to allow other operations to run
|
||||
fl::async_run();
|
||||
}
|
||||
19
.pio/libdeps/esp01_1m/FastLED/examples/Async/Async.ino
Normal file
19
.pio/libdeps/esp01_1m/FastLED/examples/Async/Async.ino
Normal file
@@ -0,0 +1,19 @@
|
||||
#include <Arduino.h>
|
||||
#include "fl/sketch_macros.h"
|
||||
|
||||
#if SKETCH_HAS_LOTS_OF_MEMORY
|
||||
|
||||
#include "./Async.h"
|
||||
|
||||
#else
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.println("Not enough memory");
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
#endif
|
||||
39
.pio/libdeps/esp01_1m/FastLED/examples/Audio/Audio.ino
Normal file
39
.pio/libdeps/esp01_1m/FastLED/examples/Audio/Audio.ino
Normal file
@@ -0,0 +1,39 @@
|
||||
/// @file Audio.ino
|
||||
/// @brief Audio visualization example with XY mapping
|
||||
/// @example Audio.ino
|
||||
///
|
||||
/// 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`
|
||||
/// 4. When the compiler is done a web page will open.
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// #define SIMPLE_EXAMPLE
|
||||
|
||||
|
||||
#include <FastLED.h>
|
||||
|
||||
#if !SKETCH_HAS_LOTS_OF_MEMORY
|
||||
// Platform does not have enough memory
|
||||
void setup() {}
|
||||
void loop() {}
|
||||
#else
|
||||
#ifdef SIMPLE_EXAMPLE
|
||||
#include "simple/simple.h"
|
||||
#else
|
||||
#include "advanced/advanced.h"
|
||||
#endif
|
||||
#endif
|
||||
148
.pio/libdeps/esp01_1m/FastLED/examples/Audio/advanced/README.md
Normal file
148
.pio/libdeps/esp01_1m/FastLED/examples/Audio/advanced/README.md
Normal file
@@ -0,0 +1,148 @@
|
||||
# Audio Reactive Visualizations
|
||||
|
||||
This example demonstrates various audio-reactive visualization modes using FastLED. It processes real-time audio input and creates stunning visual effects synchronized to music.
|
||||
|
||||
## Features
|
||||
|
||||
### Visualization Modes
|
||||
|
||||
1. **Spectrum Bars** - Classic frequency spectrum analyzer with vertical bars
|
||||
2. **Radial Spectrum** - Circular frequency visualization radiating from center
|
||||
3. **Waveform** - Real-time audio waveform display
|
||||
4. **VU Meter** - Traditional volume unit meter with RMS and peak levels
|
||||
5. **Matrix Rain** - Audio-reactive digital rain effect
|
||||
6. **Fire Effect** - Flame simulation that responds to audio intensity
|
||||
7. **Plasma Wave** - Animated plasma patterns modulated by audio
|
||||
|
||||
### Audio Processing
|
||||
|
||||
- **Real-time FFT Analysis** - Frequency spectrum analysis
|
||||
- **Beat Detection** - Automatic beat detection with adjustable sensitivity
|
||||
- **Auto Gain Control** - Automatically adjusts to varying audio levels
|
||||
- **Noise Floor Filtering** - Removes background noise
|
||||
- **Peak Detection** - Tracks audio peaks with smoothing
|
||||
|
||||
### Visual Controls
|
||||
|
||||
- **7 Color Palettes** - Rainbow, Heat, Ocean, Forest, Party, Lava, Cloud
|
||||
- **Brightness Control** - Adjustable LED brightness
|
||||
- **Fade Speed** - Control trail/persistence effects
|
||||
- **Mirror Mode** - Create symmetrical displays
|
||||
- **Beat Flash** - Visual feedback on beat detection
|
||||
|
||||
## Hardware Requirements
|
||||
|
||||
- **Controller**: ESP32, Teensy, or other platform with sufficient memory
|
||||
- **LED Matrix**: 100x100 pixels (10,000 LEDs total)
|
||||
- **Memory**: Requires `SKETCH_HAS_LOTS_OF_MEMORY` (not suitable for Arduino UNO)
|
||||
- **Audio Input**: Microphone or line-in audio source
|
||||
|
||||
## Wiring
|
||||
|
||||
```
|
||||
LED Matrix:
|
||||
- Data Pin: GPIO 3 (configurable via LED_PIN)
|
||||
- Power: 5V (ensure adequate power supply for 10,000 LEDs!)
|
||||
- Ground: Common ground with controller
|
||||
|
||||
Audio Input:
|
||||
- Follow your platform's audio input configuration
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Display Settings
|
||||
```cpp
|
||||
#define WIDTH 100 // Matrix width
|
||||
#define HEIGHT 100 // Matrix height
|
||||
#define LED_PIN 3 // Data pin for LEDs
|
||||
#define LED_TYPE WS2812B // LED chipset
|
||||
#define COLOR_ORDER GRB // Color order
|
||||
```
|
||||
|
||||
### Audio Settings
|
||||
```cpp
|
||||
#define SAMPLE_RATE 44100 // Audio sample rate
|
||||
#define FFT_SIZE 512 // FFT size for frequency analysis
|
||||
```
|
||||
|
||||
## UI Controls
|
||||
|
||||
### Master Controls
|
||||
- **Enable Audio** - Toggle audio processing on/off
|
||||
- **Visualization Mode** - Select from 7 different modes
|
||||
|
||||
### Audio Controls
|
||||
- **Audio Gain** - Manual gain adjustment (0.1 - 5.0)
|
||||
- **Noise Floor** - Background noise threshold (0.0 - 1.0)
|
||||
- **Auto Gain** - Enable automatic gain control
|
||||
|
||||
### Visual Controls
|
||||
- **Brightness** - LED brightness (0 - 255)
|
||||
- **Fade Speed** - Trail effect speed (0 - 255)
|
||||
- **Color Palette** - Choose color scheme
|
||||
- **Mirror Mode** - Enable symmetrical display
|
||||
|
||||
### Beat Detection
|
||||
- **Beat Detection** - Enable/disable beat detection
|
||||
- **Beat Sensitivity** - Adjust detection threshold (0.5 - 3.0)
|
||||
- **Beat Flash** - Enable visual flash on beats
|
||||
|
||||
## Usage
|
||||
|
||||
1. Upload the sketch to your controller
|
||||
2. Connect your LED matrix
|
||||
3. Provide audio input (microphone or line-in)
|
||||
4. Use the web UI to control visualizations
|
||||
5. Select different modes and adjust parameters in real-time
|
||||
|
||||
## Performance Tips
|
||||
|
||||
- This example requires significant processing power
|
||||
- Reduce matrix size if experiencing lag
|
||||
- Disable beat detection for lower CPU usage
|
||||
- Use simpler visualization modes on slower processors
|
||||
|
||||
## Customization
|
||||
|
||||
### Adding New Visualizations
|
||||
|
||||
1. Add your mode name to the `visualMode` dropdown
|
||||
2. Create a new `drawYourMode()` function
|
||||
3. Add a case in the main switch statement
|
||||
4. Implement your visualization logic
|
||||
|
||||
### Modifying Color Palettes
|
||||
|
||||
Edit the `getCurrentPalette()` function to add custom palettes:
|
||||
```cpp
|
||||
case 7: return YourCustomPalette_p;
|
||||
```
|
||||
|
||||
### Adjusting Matrix Size
|
||||
|
||||
For different matrix sizes, modify:
|
||||
```cpp
|
||||
#define WIDTH your_width
|
||||
#define HEIGHT your_height
|
||||
```
|
||||
|
||||
## Memory Usage
|
||||
|
||||
This example uses approximately:
|
||||
- 30KB for LED buffer (100x100 RGB)
|
||||
- 2KB for FFT data
|
||||
- 1KB for audio buffers
|
||||
- Additional memory for effect buffers
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **No visualization**: Check audio input and "Enable Audio" setting
|
||||
- **Choppy animation**: Reduce matrix size or disable some features
|
||||
- **No beat detection**: Increase beat sensitivity or check audio levels
|
||||
- **Dim display**: Increase brightness or check power supply
|
||||
- **Compilation error**: Ensure platform has sufficient memory
|
||||
|
||||
## Credits
|
||||
|
||||
This example demonstrates the audio processing capabilities of FastLED, including FFT analysis, beat detection, and various visualization techniques suitable for LED art installations, music visualizers, and interactive displays.
|
||||
562
.pio/libdeps/esp01_1m/FastLED/examples/Audio/advanced/advanced.h
Normal file
562
.pio/libdeps/esp01_1m/FastLED/examples/Audio/advanced/advanced.h
Normal file
@@ -0,0 +1,562 @@
|
||||
/// @file AudioReactive.ino
|
||||
/// @brief Audio reactive visualization with multiple modes
|
||||
/// @example AudioReactive.ino
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <FastLED.h>
|
||||
|
||||
|
||||
|
||||
#include "fl/ui.h"
|
||||
#include "fl/audio.h"
|
||||
#include "fl/fft.h"
|
||||
#include "fl/xymap.h"
|
||||
#include "fl/math.h"
|
||||
#include "fl/math_macros.h"
|
||||
|
||||
#include "fl/compiler_control.h"
|
||||
|
||||
// This is used by fastled because we have extremely strict compiler settings.
|
||||
// Stock Arduino/Platformio does not need these.
|
||||
FL_DISABLE_WARNING_PUSH
|
||||
FL_DISABLE_WARNING(float-conversion)
|
||||
FL_DISABLE_WARNING(sign-conversion)
|
||||
|
||||
|
||||
using namespace fl;
|
||||
|
||||
// Display configuration
|
||||
// For WebAssembly, use a smaller display to avoid memory issues
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#define WIDTH 32
|
||||
#define HEIGHT 32
|
||||
#else
|
||||
#define WIDTH 64
|
||||
#define HEIGHT 64
|
||||
#endif
|
||||
#define NUM_LEDS (WIDTH * HEIGHT)
|
||||
#define LED_PIN 3
|
||||
#define LED_TYPE WS2812B
|
||||
#define COLOR_ORDER GRB
|
||||
|
||||
// Audio configuration
|
||||
#define SAMPLE_RATE 44100
|
||||
#define FFT_SIZE 512
|
||||
|
||||
// UI Elements
|
||||
UITitle title("Audio Reactive Visualizations");
|
||||
UIDescription description("Real-time audio visualizations with beat detection and multiple modes");
|
||||
|
||||
// Master controls
|
||||
UICheckbox enableAudio("Enable Audio", true);
|
||||
UIDropdown visualMode("Visualization Mode",
|
||||
{"Spectrum Bars", "Radial Spectrum", "Waveform", "VU Meter", "Matrix Rain", "Fire Effect", "Plasma Wave"});
|
||||
|
||||
// Audio controls
|
||||
UISlider audioGain("Audio Gain", 1.0f, 0.1f, 5.0f, 0.1f);
|
||||
UISlider noiseFloor("Noise Floor", 0.1f, 0.0f, 1.0f, 0.01f);
|
||||
UICheckbox autoGain("Auto Gain", true);
|
||||
|
||||
// Visual controls
|
||||
UISlider brightness("Brightness", 128, 0, 255, 1);
|
||||
UISlider fadeSpeed("Fade Speed", 20, 0, 255, 1);
|
||||
UIDropdown colorPalette("Color Palette",
|
||||
{"Rainbow", "Heat", "Ocean", "Forest", "Party", "Lava", "Cloud"});
|
||||
UICheckbox mirrorMode("Mirror Mode", false);
|
||||
|
||||
// Beat detection
|
||||
UICheckbox beatDetect("Beat Detection", true);
|
||||
UISlider beatSensitivity("Beat Sensitivity", 1.5f, 0.5f, 3.0f, 0.1f);
|
||||
UICheckbox beatFlash("Beat Flash", true);
|
||||
|
||||
// Audio input
|
||||
UIAudio audio("Audio Input");
|
||||
|
||||
// Global variables
|
||||
CRGB leds[NUM_LEDS];
|
||||
XYMap xyMap(WIDTH, HEIGHT, false);
|
||||
SoundLevelMeter soundMeter(0.0, 0.0);
|
||||
|
||||
// Audio processing variables - keep these smaller for WebAssembly
|
||||
static const int NUM_BANDS = 16; // Reduced from 32
|
||||
float fftSmooth[NUM_BANDS] = {0};
|
||||
float beatHistory[20] = {0}; // Reduced from 43
|
||||
int beatHistoryIndex = 0;
|
||||
float beatAverage = 0;
|
||||
float beatVariance = 0;
|
||||
uint32_t lastBeatTime = 0;
|
||||
bool isBeat = false;
|
||||
float autoGainValue = 1.0f;
|
||||
float peakLevel = 0;
|
||||
|
||||
// Visual effect variables
|
||||
uint8_t hue = 0;
|
||||
// Remove large static arrays for WebAssembly
|
||||
#ifndef __EMSCRIPTEN__
|
||||
float plasma[WIDTH][HEIGHT] = {{0}};
|
||||
uint8_t fireBuffer[WIDTH][HEIGHT] = {{0}};
|
||||
#endif
|
||||
|
||||
// Get current color palette
|
||||
CRGBPalette16 getCurrentPalette() {
|
||||
switch(colorPalette.as_int()) {
|
||||
case 0: return CRGBPalette16(RainbowColors_p);
|
||||
case 1: return CRGBPalette16(HeatColors_p);
|
||||
case 2: return CRGBPalette16(OceanColors_p);
|
||||
case 3: return CRGBPalette16(ForestColors_p);
|
||||
case 4: return CRGBPalette16(PartyColors_p);
|
||||
case 5: return CRGBPalette16(LavaColors_p);
|
||||
case 6: return CRGBPalette16(CloudColors_p);
|
||||
default: return CRGBPalette16(RainbowColors_p);
|
||||
}
|
||||
}
|
||||
|
||||
// Beat detection algorithm
|
||||
bool detectBeat(float energy) {
|
||||
beatHistory[beatHistoryIndex] = energy;
|
||||
beatHistoryIndex = (beatHistoryIndex + 1) % 20;
|
||||
|
||||
// Calculate average
|
||||
beatAverage = 0;
|
||||
for (int i = 0; i < 20; i++) {
|
||||
beatAverage += beatHistory[i];
|
||||
}
|
||||
beatAverage /= 20.0f;
|
||||
|
||||
// Calculate variance
|
||||
beatVariance = 0;
|
||||
for (int i = 0; i < 20; i++) {
|
||||
float diff = beatHistory[i] - beatAverage;
|
||||
beatVariance += diff * diff;
|
||||
}
|
||||
beatVariance /= 20.0f;
|
||||
|
||||
// Detect beat
|
||||
float threshold = beatAverage + (beatSensitivity.value() * sqrt(beatVariance));
|
||||
uint32_t currentTime = millis();
|
||||
|
||||
if (energy > threshold && (currentTime - lastBeatTime) > 80) {
|
||||
lastBeatTime = currentTime;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update auto gain
|
||||
void updateAutoGain(float level) {
|
||||
if (!autoGain) {
|
||||
autoGainValue = 1.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
static float targetLevel = 0.7f;
|
||||
static float avgLevel = 0.0f;
|
||||
|
||||
avgLevel = avgLevel * 0.95f + level * 0.05f;
|
||||
|
||||
if (avgLevel > 0.01f) {
|
||||
float gainAdjust = targetLevel / avgLevel;
|
||||
gainAdjust = fl::clamp(gainAdjust, 0.5f, 2.0f);
|
||||
autoGainValue = autoGainValue * 0.9f + gainAdjust * 0.1f;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear display
|
||||
void clearDisplay() {
|
||||
if (fadeSpeed.as_int() == 0) {
|
||||
fill_solid(leds, NUM_LEDS, CRGB::Black);
|
||||
} else {
|
||||
fadeToBlackBy(leds, NUM_LEDS, fadeSpeed.as_int());
|
||||
}
|
||||
}
|
||||
|
||||
// Visualization: Spectrum Bars
|
||||
void drawSpectrumBars(FFTBins* fft, float /* peak */) {
|
||||
clearDisplay();
|
||||
CRGBPalette16 palette = getCurrentPalette();
|
||||
|
||||
int barWidth = WIDTH / NUM_BANDS;
|
||||
|
||||
for (size_t band = 0; band < NUM_BANDS && band < fft->bins_db.size(); band++) {
|
||||
float magnitude = fft->bins_db[band];
|
||||
|
||||
// Apply noise floor
|
||||
magnitude = magnitude / 100.0f; // Normalize from dB
|
||||
magnitude = MAX(0.0f, magnitude - noiseFloor.value());
|
||||
|
||||
// Smooth the FFT
|
||||
fftSmooth[band] = fftSmooth[band] * 0.8f + magnitude * 0.2f;
|
||||
magnitude = fftSmooth[band];
|
||||
|
||||
// Apply gain
|
||||
magnitude *= audioGain.value() * autoGainValue;
|
||||
magnitude = fl::clamp(magnitude, 0.0f, 1.0f);
|
||||
|
||||
int barHeight = magnitude * HEIGHT;
|
||||
int xStart = band * barWidth;
|
||||
|
||||
for (int x = 0; x < barWidth - 1; x++) {
|
||||
for (int y = 0; y < barHeight; y++) {
|
||||
uint8_t colorIndex = fl::map_range<float, uint8_t>(
|
||||
float(y) / HEIGHT, 0, 1, 0, 255
|
||||
);
|
||||
CRGB color = ColorFromPalette(palette, colorIndex + hue);
|
||||
|
||||
int ledIndex = xyMap(xStart + x, y);
|
||||
if (ledIndex >= 0 && ledIndex < NUM_LEDS) {
|
||||
leds[ledIndex] = color;
|
||||
}
|
||||
|
||||
if (mirrorMode) {
|
||||
int mirrorIndex = xyMap(WIDTH - 1 - (xStart + x), y);
|
||||
if (mirrorIndex >= 0 && mirrorIndex < NUM_LEDS) {
|
||||
leds[mirrorIndex] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Visualization: Radial Spectrum
|
||||
void drawRadialSpectrum(FFTBins* fft, float /* peak */) {
|
||||
clearDisplay();
|
||||
CRGBPalette16 palette = getCurrentPalette();
|
||||
|
||||
int centerX = WIDTH / 2;
|
||||
int centerY = HEIGHT / 2;
|
||||
|
||||
for (size_t angle = 0; angle < 360; angle += 6) { // Reduced resolution
|
||||
size_t band = (angle / 6) % NUM_BANDS;
|
||||
if (band >= fft->bins_db.size()) continue;
|
||||
|
||||
float magnitude = fft->bins_db[band] / 100.0f;
|
||||
magnitude = MAX(0.0f, magnitude - noiseFloor.value());
|
||||
magnitude *= audioGain.value() * autoGainValue;
|
||||
magnitude = fl::clamp(magnitude, 0.0f, 1.0f);
|
||||
|
||||
int radius = magnitude * (MIN(WIDTH, HEIGHT) / 2);
|
||||
|
||||
for (int r = 0; r < radius; r++) {
|
||||
int x = centerX + (r * cosf(angle * PI / 180.0f));
|
||||
int y = centerY + (r * sinf(angle * PI / 180.0f));
|
||||
|
||||
if (x >= 0 && x < WIDTH && y >= 0 && y < HEIGHT) {
|
||||
uint8_t colorIndex = fl::map_range<int, uint8_t>(r, 0, radius, 255, 0);
|
||||
int ledIndex = xyMap(x, y);
|
||||
if (ledIndex >= 0 && ledIndex < NUM_LEDS) {
|
||||
leds[ledIndex] = ColorFromPalette(palette, colorIndex + hue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Visualization: Logarithmic Waveform (prevents saturation)
|
||||
void drawWaveform(const Slice<const int16_t>& pcm, float /* peak */) {
|
||||
clearDisplay();
|
||||
CRGBPalette16 palette = getCurrentPalette();
|
||||
|
||||
int samplesPerPixel = pcm.size() / WIDTH;
|
||||
int centerY = HEIGHT / 2;
|
||||
|
||||
for (size_t x = 0; x < WIDTH; x++) {
|
||||
size_t sampleIndex = x * samplesPerPixel;
|
||||
if (sampleIndex >= pcm.size()) break;
|
||||
|
||||
// Get the raw sample value
|
||||
float sample = float(pcm[sampleIndex]) / 32768.0f; // Normalize to -1.0 to 1.0
|
||||
|
||||
// Apply logarithmic scaling to prevent saturation
|
||||
float absSample = fabsf(sample);
|
||||
float logAmplitude = 0.0f;
|
||||
|
||||
if (absSample > 0.001f) { // Avoid log(0)
|
||||
// Logarithmic compression: log10(1 + gain * sample)
|
||||
float scaledSample = absSample * audioGain.value() * autoGainValue;
|
||||
logAmplitude = log10f(1.0f + scaledSample * 9.0f) / log10f(10.0f); // Normalize to 0-1
|
||||
}
|
||||
|
||||
// Apply smooth sensitivity curve
|
||||
logAmplitude = powf(logAmplitude, 0.7f); // Gamma correction for better visual response
|
||||
|
||||
// Calculate amplitude in pixels
|
||||
int amplitude = int(logAmplitude * (HEIGHT / 2));
|
||||
amplitude = fl::clamp(amplitude, 0, HEIGHT / 2);
|
||||
|
||||
// Preserve the sign for proper waveform display
|
||||
if (sample < 0) amplitude = -amplitude;
|
||||
|
||||
// Color mapping based on amplitude intensity
|
||||
uint8_t colorIndex = fl::map_range<int, uint8_t>(abs(amplitude), 0, HEIGHT/2, 40, 255);
|
||||
CRGB color = ColorFromPalette(palette, colorIndex + hue);
|
||||
|
||||
// Apply brightness scaling for low amplitudes
|
||||
if (abs(amplitude) < HEIGHT / 4) {
|
||||
color.fadeToBlackBy(128 - (abs(amplitude) * 512 / HEIGHT));
|
||||
}
|
||||
|
||||
// Draw vertical line from center
|
||||
if (amplitude == 0) {
|
||||
// Draw center point for zero amplitude
|
||||
int ledIndex = xyMap(x, centerY);
|
||||
if (ledIndex >= 0 && ledIndex < NUM_LEDS) {
|
||||
leds[ledIndex] = color.fadeToBlackBy(200);
|
||||
}
|
||||
} else {
|
||||
// Draw line from center to amplitude
|
||||
int startY = (amplitude > 0) ? centerY : centerY + amplitude;
|
||||
int endY = (amplitude > 0) ? centerY + amplitude : centerY;
|
||||
|
||||
for (int y = startY; y <= endY; y++) {
|
||||
if (y >= 0 && y < HEIGHT) {
|
||||
int ledIndex = xyMap(x, y);
|
||||
if (ledIndex >= 0 && ledIndex < NUM_LEDS) {
|
||||
// Fade edges for smoother appearance
|
||||
CRGB pixelColor = color;
|
||||
if (y == startY || y == endY) {
|
||||
pixelColor.fadeToBlackBy(100);
|
||||
}
|
||||
leds[ledIndex] = pixelColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Visualization: VU Meter
|
||||
void drawVUMeter(float rms, float peak) {
|
||||
clearDisplay();
|
||||
CRGBPalette16 palette = getCurrentPalette();
|
||||
|
||||
// RMS level bar
|
||||
int rmsWidth = rms * WIDTH * audioGain.value() * autoGainValue;
|
||||
rmsWidth = MIN(rmsWidth, WIDTH);
|
||||
|
||||
for (int x = 0; x < rmsWidth; x++) {
|
||||
for (int y = HEIGHT/3; y < 2*HEIGHT/3; y++) {
|
||||
uint8_t colorIndex = fl::map_range<int, uint8_t>(x, 0, WIDTH, 0, 255);
|
||||
int ledIndex = xyMap(x, y);
|
||||
if (ledIndex >= 0 && ledIndex < NUM_LEDS) {
|
||||
leds[ledIndex] = ColorFromPalette(palette, colorIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Peak indicator
|
||||
int peakX = peak * WIDTH * audioGain.value() * autoGainValue;
|
||||
peakX = MIN(peakX, WIDTH - 1);
|
||||
|
||||
for (int y = HEIGHT/4; y < 3*HEIGHT/4; y++) {
|
||||
int ledIndex = xyMap(peakX, y);
|
||||
if (ledIndex >= 0 && ledIndex < NUM_LEDS) {
|
||||
leds[ledIndex] = CRGB::White;
|
||||
}
|
||||
}
|
||||
|
||||
// Beat indicator
|
||||
if (isBeat && beatFlash) {
|
||||
for (int x = 0; x < WIDTH; x++) {
|
||||
int ledIndex1 = xyMap(x, 0);
|
||||
int ledIndex2 = xyMap(x, HEIGHT - 1);
|
||||
if (ledIndex1 >= 0 && ledIndex1 < NUM_LEDS) leds[ledIndex1] = CRGB::White;
|
||||
if (ledIndex2 >= 0 && ledIndex2 < NUM_LEDS) leds[ledIndex2] = CRGB::White;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Visualization: Matrix Rain
|
||||
void drawMatrixRain(float peak) {
|
||||
// Shift everything down
|
||||
for (int x = 0; x < WIDTH; x++) {
|
||||
for (int y = HEIGHT - 1; y > 0; y--) {
|
||||
int currentIndex = xyMap(x, y);
|
||||
int aboveIndex = xyMap(x, y - 1);
|
||||
if (currentIndex >= 0 && currentIndex < NUM_LEDS &&
|
||||
aboveIndex >= 0 && aboveIndex < NUM_LEDS) {
|
||||
leds[currentIndex] = leds[aboveIndex];
|
||||
leds[currentIndex].fadeToBlackBy(40);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add new drops based on audio
|
||||
int numDrops = peak * WIDTH * audioGain.value() * autoGainValue;
|
||||
for (int i = 0; i < numDrops; i++) {
|
||||
int x = random(WIDTH);
|
||||
int ledIndex = xyMap(x, 0);
|
||||
if (ledIndex >= 0 && ledIndex < NUM_LEDS) {
|
||||
leds[ledIndex] = CHSV(96, 255, 255); // Green
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Visualization: Fire Effect (simplified for WebAssembly)
|
||||
void drawFireEffect(float peak) {
|
||||
// Simple fire effect without buffer
|
||||
clearDisplay();
|
||||
|
||||
// Add heat at bottom based on audio
|
||||
int heat = 100 + (peak * 155 * audioGain.value() * autoGainValue);
|
||||
heat = MIN(heat, 255);
|
||||
|
||||
for (int x = 0; x < WIDTH; x++) {
|
||||
for (int y = 0; y < HEIGHT; y++) {
|
||||
// Simple gradient from bottom to top
|
||||
int heatLevel = heat * (HEIGHT - y) / HEIGHT;
|
||||
heatLevel = heatLevel * random(80, 120) / 100; // Add randomness
|
||||
heatLevel = MIN(heatLevel, 255);
|
||||
|
||||
int ledIndex = xyMap(x, y);
|
||||
if (ledIndex >= 0 && ledIndex < NUM_LEDS) {
|
||||
leds[ledIndex] = HeatColor(heatLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Visualization: Plasma Wave
|
||||
void drawPlasmaWave(float peak) {
|
||||
static float time = 0;
|
||||
time += 0.05f + (peak * 0.2f);
|
||||
|
||||
CRGBPalette16 palette = getCurrentPalette();
|
||||
|
||||
for (int x = 0; x < WIDTH; x++) {
|
||||
for (int y = 0; y < HEIGHT; y++) {
|
||||
float value = sinf(x * 0.1f + time) +
|
||||
sinf(y * 0.1f - time) +
|
||||
sinf((x + y) * 0.1f + time) +
|
||||
sinf(sqrtf(x * x + y * y) * 0.1f - time);
|
||||
|
||||
value = (value + 4) / 8; // Normalize to 0-1
|
||||
value *= audioGain.value() * autoGainValue;
|
||||
|
||||
uint8_t colorIndex = value * 255;
|
||||
int ledIndex = xyMap(x, y);
|
||||
if (ledIndex >= 0 && ledIndex < NUM_LEDS) {
|
||||
leds[ledIndex] = ColorFromPalette(palette, colorIndex + hue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
delay(1000);
|
||||
|
||||
Serial.println("Audio Reactive Visualizations");
|
||||
Serial.println("Initializing...");
|
||||
Serial.print("Display size: ");
|
||||
Serial.print(WIDTH);
|
||||
Serial.print("x");
|
||||
Serial.println(HEIGHT);
|
||||
|
||||
// Initialize LEDs
|
||||
FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS);
|
||||
FastLED.setBrightness(brightness.as_int());
|
||||
FastLED.clear();
|
||||
FastLED.show();
|
||||
|
||||
// Set up UI callbacks
|
||||
brightness.onChanged([](float value) {
|
||||
FastLED.setBrightness(value);
|
||||
});
|
||||
|
||||
Serial.println("Setup complete!");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Check if audio is enabled
|
||||
if (!enableAudio) {
|
||||
// Show a simple test pattern
|
||||
fill_rainbow(leds, NUM_LEDS, hue++, 7);
|
||||
FastLED.show();
|
||||
delay(20);
|
||||
return;
|
||||
}
|
||||
|
||||
// Process only one audio sample per frame to avoid accumulation
|
||||
AudioSample sample = audio.next();
|
||||
if (sample.isValid()) {
|
||||
// Update sound meter
|
||||
soundMeter.processBlock(sample.pcm());
|
||||
|
||||
// Get audio levels
|
||||
float rms = sample.rms() / 32768.0f;
|
||||
|
||||
// Calculate peak
|
||||
int32_t maxSample = 0;
|
||||
for (size_t i = 0; i < sample.pcm().size(); i++) {
|
||||
int32_t absSample = fabsf(sample.pcm()[i]);
|
||||
if (absSample > maxSample) {
|
||||
maxSample = absSample;
|
||||
}
|
||||
}
|
||||
float peak = float(maxSample) / 32768.0f;
|
||||
peakLevel = peakLevel * 0.9f + peak * 0.1f; // Smooth peak
|
||||
|
||||
// Update auto gain
|
||||
updateAutoGain(rms);
|
||||
|
||||
// Beat detection
|
||||
if (beatDetect) {
|
||||
isBeat = detectBeat(peak);
|
||||
}
|
||||
|
||||
// Get FFT data - create local FFTBins to avoid accumulation
|
||||
FFTBins fftBins(NUM_BANDS);
|
||||
sample.fft(&fftBins);
|
||||
|
||||
// Update color animation
|
||||
hue += 1;
|
||||
|
||||
// Apply beat flash
|
||||
if (isBeat && beatFlash) {
|
||||
for (int i = 0; i < NUM_LEDS; i++) {
|
||||
leds[i].fadeLightBy(-50); // Make brighter
|
||||
}
|
||||
}
|
||||
|
||||
// Draw selected visualization
|
||||
switch (visualMode.as_int()) {
|
||||
case 0: // Spectrum Bars
|
||||
drawSpectrumBars(&fftBins, peakLevel);
|
||||
break;
|
||||
|
||||
case 1: // Radial Spectrum
|
||||
drawRadialSpectrum(&fftBins, peakLevel);
|
||||
break;
|
||||
|
||||
case 2: // Waveform
|
||||
drawWaveform(sample.pcm(), peakLevel);
|
||||
break;
|
||||
|
||||
case 3: // VU Meter
|
||||
drawVUMeter(rms, peakLevel);
|
||||
break;
|
||||
|
||||
case 4: // Matrix Rain
|
||||
drawMatrixRain(peakLevel);
|
||||
break;
|
||||
|
||||
case 5: // Fire Effect
|
||||
drawFireEffect(peakLevel);
|
||||
break;
|
||||
|
||||
case 6: // Plasma Wave
|
||||
drawPlasmaWave(peakLevel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
FastLED.show();
|
||||
|
||||
// Add a small delay to prevent tight loops in WebAssembly
|
||||
#ifdef __EMSCRIPTEN__
|
||||
delay(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
FL_DISABLE_WARNING_POP
|
||||
157
.pio/libdeps/esp01_1m/FastLED/examples/Audio/simple/README.md
Normal file
157
.pio/libdeps/esp01_1m/FastLED/examples/Audio/simple/README.md
Normal file
@@ -0,0 +1,157 @@
|
||||
# Audio Reactive Visualization Example
|
||||
|
||||
This example demonstrates advanced audio reactive visualization capabilities using FastLED. It processes real-time audio input and creates stunning visual effects synchronized to music.
|
||||
|
||||
## Features
|
||||
|
||||
### Visualization Modes
|
||||
|
||||
1. **Spectrum Analyzer** - Classic frequency spectrum display with customizable colors
|
||||
2. **Waveform** - Real-time audio waveform visualization
|
||||
3. **VU Meter** - Traditional volume unit meter with RMS and peak indicators
|
||||
4. **Spectrogram** - Scrolling frequency analysis over time
|
||||
5. **Combined** - Split-screen showing both spectrum and waveform
|
||||
6. **Reactive Patterns** - Dynamic patterns that respond to audio energy and beats
|
||||
|
||||
### Audio Processing
|
||||
|
||||
- **Real-time FFT** - Fast Fourier Transform for frequency analysis
|
||||
- **Beat Detection** - Automatic beat detection with adjustable sensitivity
|
||||
- **Auto Gain Control (AGC)** - Automatically adjusts to varying audio levels
|
||||
- **Noise Floor Filtering** - Removes background noise for cleaner visuals
|
||||
- **Attack/Decay/Sustain** - Professional audio envelope controls
|
||||
|
||||
### Visual Controls
|
||||
|
||||
- **Multiple Color Palettes** - Heat, Rainbow, Ocean, Forest, Lava, Cloud, Party
|
||||
- **Mirror Mode** - Creates symmetrical displays
|
||||
- **Brightness Control** - Adjustable LED brightness
|
||||
- **Fade Effects** - Smooth transitions with adjustable fade time
|
||||
- **Color Animation** - Animated color cycling with speed control
|
||||
- **Smoothing** - Optional smoothing for less jittery displays
|
||||
|
||||
### Advanced Features
|
||||
|
||||
- **Frequency Band Analysis** - 8-band frequency analyzer for detailed audio analysis
|
||||
- **FFT Smoothing** - Temporal smoothing of frequency data
|
||||
- **Logarithmic Scale** - Optional log scale for frequency display
|
||||
- **Freeze Frame** - Pause the visualization at any moment
|
||||
- **Frame Advance** - Step through frozen frames
|
||||
|
||||
## UI Controls
|
||||
|
||||
### Main Controls
|
||||
- **Enable Audio Reactive Mode** - Master on/off switch for audio processing
|
||||
- **Visualization Mode** - Dropdown to select visualization type
|
||||
|
||||
### Audio Processing Group
|
||||
- **Fade Time** - How quickly levels decay (0-4 seconds)
|
||||
- **Attack Time** - How quickly levels rise (0-4 seconds)
|
||||
- **Output Smoothing** - Final output smoothing (0-2 seconds)
|
||||
- **Audio Gain** - Manual gain adjustment (0.1-5.0)
|
||||
- **Noise Floor** - Background noise threshold (-80 to -20 dB)
|
||||
|
||||
### Visual Controls Group
|
||||
- **Fade to Black** - Trail/persistence effect (0-50)
|
||||
- **Brightness** - LED brightness (0-255)
|
||||
- **Color Speed** - Animation speed (0.1-5.0)
|
||||
- **Color Palette** - Choose from 7 palettes
|
||||
- **Mirror Mode** - Enable symmetrical display
|
||||
- **Smoothing** - Enable temporal smoothing
|
||||
|
||||
### FFT Controls Group
|
||||
- **Min Frequency** - Lower frequency bound (20-1000 Hz)
|
||||
- **Max Frequency** - Upper frequency bound (1000-20000 Hz)
|
||||
- **Logarithmic Scale** - Use log scale for frequency
|
||||
- **FFT Smoothing** - Smoothing factor (0-0.95)
|
||||
|
||||
### Advanced Controls Group
|
||||
- **Freeze Frame** - Pause visualization
|
||||
- **Advance Frame** - Step forward when frozen
|
||||
- **Beat Detection** - Enable beat detection
|
||||
- **Beat Sensitivity** - Beat detection threshold (0.5-3.0)
|
||||
- **Auto Gain Control** - Enable automatic gain adjustment
|
||||
|
||||
## Hardware Setup
|
||||
|
||||
### LED Configuration
|
||||
- Default: 128x128 LED matrix (16,384 LEDs)
|
||||
- Downscaled to 64x64 for output (4,096 LEDs)
|
||||
- Data pin: GPIO 3 (configurable)
|
||||
- LED type: WS2812B (Neopixel)
|
||||
|
||||
### Audio Input
|
||||
The example uses the FastLED audio system which can accept input from:
|
||||
- Microphone (real-time audio capture)
|
||||
- Audio file playback
|
||||
- System audio (on supported platforms)
|
||||
|
||||
## Usage
|
||||
|
||||
1. **Basic Operation**
|
||||
- Upload the sketch to your controller
|
||||
- Connect your LED matrix
|
||||
- Provide audio input
|
||||
- Use the web UI to control visualization
|
||||
|
||||
2. **Optimizing for Your Setup**
|
||||
- Adjust the noise floor if visualization is too sensitive/insensitive
|
||||
- Use AGC for varying audio levels
|
||||
- Tune beat sensitivity for your music style
|
||||
- Experiment with different color palettes and speeds
|
||||
|
||||
3. **Performance Tips**
|
||||
- Reduce matrix size for slower controllers
|
||||
- Disable smoothing for more responsive display
|
||||
- Use simpler visualization modes for lower CPU usage
|
||||
|
||||
## Code Structure
|
||||
|
||||
### Main Components
|
||||
|
||||
1. **Audio Processing Pipeline**
|
||||
```cpp
|
||||
AudioSample → FFT → Band Analysis → Beat Detection → Visualization
|
||||
```
|
||||
|
||||
2. **Visualization Functions**
|
||||
- `drawSpectrumAnalyzer()` - Frequency spectrum bars
|
||||
- `drawWaveform()` - Audio waveform display
|
||||
- `drawVUMeter()` - Volume meter visualization
|
||||
- `drawSpectrogram()` - Time-frequency plot
|
||||
- `drawReactivePatterns()` - Beat-reactive patterns
|
||||
|
||||
3. **Audio Analysis Classes**
|
||||
- `MaxFadeTracker` - Smooth peak tracking with attack/decay
|
||||
- `BeatDetector` - Energy-based beat detection
|
||||
- `FrequencyBandAnalyzer` - 8-band frequency analysis
|
||||
|
||||
## Customization
|
||||
|
||||
### Adding New Visualizations
|
||||
1. Create a new draw function
|
||||
2. Add it to the visualization mode dropdown
|
||||
3. Add a case in the main switch statement
|
||||
|
||||
### Modifying Color Palettes
|
||||
Edit the `getCurrentPalette()` function to add custom palettes.
|
||||
|
||||
### Adjusting Frequency Bands
|
||||
Modify the `FrequencyBandAnalyzer` constructor to change band boundaries.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **No visualization**: Check audio input and ensure "Enable Audio Reactive Mode" is on
|
||||
- **Too dim/bright**: Adjust brightness control
|
||||
- **Choppy animation**: Increase smoothing or reduce matrix size
|
||||
- **No beat detection**: Adjust beat sensitivity or check audio levels
|
||||
- **Visualization too sensitive**: Increase noise floor value
|
||||
|
||||
## Memory Requirements
|
||||
|
||||
This example requires significant memory for:
|
||||
- Framebuffer: 128×128×3 = 49,152 bytes
|
||||
- LED buffer: 64×64×3 = 12,288 bytes
|
||||
- Audio buffers and FFT data
|
||||
|
||||
Platforms with limited memory may need to reduce the matrix size.
|
||||
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
#include "fl/time_alpha.h"
|
||||
#include "fl/math_macros.h"
|
||||
|
||||
/// Tracks a smoothed peak with attack, decay, and output-inertia time-constants.
|
||||
class MaxFadeTracker {
|
||||
public:
|
||||
/// @param attackTimeSec τ₁: how quickly to rise toward a new peak.
|
||||
/// @param decayTimeSec τ₂: how quickly to decay to 1/e of value.
|
||||
/// @param outputTimeSec τ₃: how quickly the returned value follows currentLevel_.
|
||||
/// @param sampleRate audio sample rate (e.g. 44100 or 48000).
|
||||
MaxFadeTracker(float attackTimeSec,
|
||||
float decayTimeSec,
|
||||
float outputTimeSec,
|
||||
float sampleRate)
|
||||
: attackRate_(1.0f / attackTimeSec)
|
||||
, decayRate_(1.0f / decayTimeSec)
|
||||
, outputRate_(1.0f / outputTimeSec)
|
||||
, sampleRate_(sampleRate)
|
||||
, currentLevel_(0.0f)
|
||||
, smoothedOutput_(0.0f)
|
||||
{}
|
||||
|
||||
void setAttackTime(float t){ attackRate_ = 1.0f/t; }
|
||||
void setDecayTime (float t){ decayRate_ = 1.0f/t; }
|
||||
void setOutputTime(float t){ outputRate_ = 1.0f/t; }
|
||||
|
||||
/// Process one 512-sample block; returns [0…1] with inertia.
|
||||
float operator()(const int16_t* samples, size_t length) {
|
||||
assert(length == 512);
|
||||
// 1) block peak
|
||||
float peak = 0.0f;
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
float v = ABS(samples[i]) * (1.0f/32768.0f);
|
||||
peak = MAX(peak, v);
|
||||
}
|
||||
|
||||
// 2) time delta
|
||||
float dt = static_cast<float>(length) / sampleRate_;
|
||||
|
||||
// 3) update currentLevel_ with attack/decay
|
||||
if (peak > currentLevel_) {
|
||||
float riseFactor = 1.0f - fl::exp(-attackRate_ * dt);
|
||||
currentLevel_ += (peak - currentLevel_) * riseFactor;
|
||||
} else {
|
||||
float decayFactor = fl::exp(-decayRate_ * dt);
|
||||
currentLevel_ *= decayFactor;
|
||||
}
|
||||
|
||||
// 4) output inertia: smooth smoothedOutput_ → currentLevel_
|
||||
float outFactor = 1.0f - fl::exp(-outputRate_ * dt);
|
||||
smoothedOutput_ += (currentLevel_ - smoothedOutput_) * outFactor;
|
||||
|
||||
return smoothedOutput_;
|
||||
}
|
||||
|
||||
private:
|
||||
float attackRate_; // = 1/τ₁
|
||||
float decayRate_; // = 1/τ₂
|
||||
float outputRate_; // = 1/τ₃
|
||||
float sampleRate_;
|
||||
float currentLevel_; // instantaneous peak with attack/decay
|
||||
float smoothedOutput_; // returned value with inertia
|
||||
};
|
||||
253
.pio/libdeps/esp01_1m/FastLED/examples/Audio/simple/simple.h
Normal file
253
.pio/libdeps/esp01_1m/FastLED/examples/Audio/simple/simple.h
Normal file
@@ -0,0 +1,253 @@
|
||||
/// @file Audio.ino
|
||||
/// @brief Audio visualization example with XY mapping
|
||||
/// @example Audio.ino
|
||||
///
|
||||
/// 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`
|
||||
/// 4. When the compiler is done a web page will open.
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <FastLED.h>
|
||||
|
||||
#include "fl/audio.h"
|
||||
#include "fl/downscale.h"
|
||||
#include "fl/draw_visitor.h"
|
||||
#include "fl/fft.h"
|
||||
#include "fl/math.h"
|
||||
#include "fl/math_macros.h"
|
||||
#include "fl/raster.h"
|
||||
#include "fl/time_alpha.h"
|
||||
#include "fl/ui.h"
|
||||
#include "fl/xypath.h"
|
||||
#include "fl/unused.h"
|
||||
#include "fx/time.h"
|
||||
#include "fl/function.h"
|
||||
|
||||
// Sketch.
|
||||
#include "fx_audio.h"
|
||||
|
||||
#include "fl/memfill.h"
|
||||
using namespace fl;
|
||||
|
||||
#define HEIGHT 128
|
||||
#define WIDTH 128
|
||||
#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.");
|
||||
UICheckbox enableVolumeVis("Enable volume visualization", false);
|
||||
UICheckbox enableRMS("Enable RMS visualization", false);
|
||||
UICheckbox enableFFT("Enable FFT visualization", true);
|
||||
UICheckbox freeze("Freeze frame", false);
|
||||
UIButton advanceFrame("Advance frame");
|
||||
UISlider decayTimeSeconds("Fade time Seconds", .1, 0, 4, .02);
|
||||
UISlider attackTimeSeconds("Attack time Seconds", .1, 0, 4, .02);
|
||||
UISlider outputTimeSec("outputTimeSec", .17, 0, 2, .01);
|
||||
|
||||
UIAudio audio("Audio");
|
||||
UISlider fadeToBlack("Fade to black by", 5, 0, 20, 1);
|
||||
|
||||
// Group related UI elements using UIGroup template multi-argument constructor
|
||||
UIGroup visualizationControls("Visualization", enableVolumeVis, enableRMS, enableFFT);
|
||||
UIGroup audioProcessingControls("Audio Processing", decayTimeSeconds, attackTimeSeconds, outputTimeSec);
|
||||
UIGroup generalControls("General Controls", freeze, advanceFrame, fadeToBlack);
|
||||
|
||||
MaxFadeTracker audioFadeTracker(attackTimeSeconds.value(),
|
||||
decayTimeSeconds.value(), outputTimeSec.value(),
|
||||
44100);
|
||||
|
||||
CRGB framebuffer[NUM_LEDS];
|
||||
XYMap frameBufferXY(WIDTH, HEIGHT, IS_SERPINTINE);
|
||||
|
||||
CRGB leds[NUM_LEDS / 4]; // Downscaled buffer
|
||||
XYMap ledsXY(WIDTH / 2, HEIGHT / 2,
|
||||
IS_SERPINTINE); // Framebuffer is regular rectangle LED matrix.
|
||||
|
||||
FFTBins fftOut(WIDTH); // 2x width due to super sampling.
|
||||
|
||||
// CRGB framebuffer[NUM_LEDS];
|
||||
// CRGB framebuffer[WIDTH_2X * HEIGHT_2X]; // 2x super sampling.
|
||||
// XYMap frameBufferXY(WIDTH, HEIGHT, IS_SERPINTINE); // LED output, serpentine
|
||||
// as is common for LED matrices. XYMap xyMap_2X(WIDTH_2X, HEIGHT_2X, false); //
|
||||
// Framebuffer is regular rectangle LED matrix.
|
||||
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
bool triggered = false;
|
||||
|
||||
SoundLevelMeter soundLevelMeter(.0, 0.0);
|
||||
|
||||
float rms(Slice<const int16_t> data) {
|
||||
double sumSq = 0.0;
|
||||
const int N = data.size();
|
||||
for (int i = 0; i < N; ++i) {
|
||||
int32_t x32 = int32_t(data[i]);
|
||||
sumSq += x32 * x32;
|
||||
}
|
||||
float rms = sqrt(float(sumSq) / N);
|
||||
return rms;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
// auto screenmap = frameBufferXY.toScreenMap();
|
||||
// screenmap.setDiameter(.2);
|
||||
// FastLED.addLeds<NEOPIXEL, 2>(framebuffer,
|
||||
// NUM_LEDS).setScreenMap(screenmap);
|
||||
auto screenmap = ledsXY.toScreenMap();
|
||||
screenmap.setDiameter(.2);
|
||||
|
||||
decayTimeSeconds.onChanged([](float value) {
|
||||
audioFadeTracker.setDecayTime(value);
|
||||
FASTLED_WARN("Fade time seconds: " << value);
|
||||
});
|
||||
attackTimeSeconds.onChanged([](float value) {
|
||||
audioFadeTracker.setAttackTime(value);
|
||||
FASTLED_WARN("Attack time seconds: " << value);
|
||||
});
|
||||
outputTimeSec.onChanged([](float value) {
|
||||
audioFadeTracker.setOutputTime(value);
|
||||
FASTLED_WARN("Output time seconds: " << value);
|
||||
});
|
||||
FastLED.addLeds<NEOPIXEL, PIN_DATA>(leds, ledsXY.getTotal())
|
||||
.setScreenMap(screenmap);
|
||||
}
|
||||
|
||||
void shiftUp() {
|
||||
// fade each led by 1%
|
||||
if (fadeToBlack.as_int()) {
|
||||
|
||||
for (int i = 0; i < NUM_LEDS; ++i) {
|
||||
auto &c = framebuffer[i];
|
||||
c.fadeToBlackBy(fadeToBlack.as_int());
|
||||
}
|
||||
}
|
||||
|
||||
for (int y = HEIGHT - 1; y > 0; --y) {
|
||||
CRGB* row1 = &framebuffer[frameBufferXY(0, y)];
|
||||
CRGB* row2 = &framebuffer[frameBufferXY(0, y - 1)];
|
||||
memcpy(row1, row2, WIDTH * sizeof(CRGB));
|
||||
}
|
||||
CRGB* row = &framebuffer[frameBufferXY(0, 0)];
|
||||
fl::memfill(row, 0, sizeof(CRGB) * WIDTH);
|
||||
}
|
||||
|
||||
|
||||
bool doFrame() {
|
||||
if (!freeze) {
|
||||
return true;
|
||||
}
|
||||
if (advanceFrame.isPressed()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (triggered) {
|
||||
FASTLED_WARN("Triggered");
|
||||
}
|
||||
|
||||
// x = pointX.as_int();
|
||||
y = HEIGHT / 2;
|
||||
|
||||
bool do_frame = doFrame();
|
||||
|
||||
while (AudioSample sample = audio.next()) {
|
||||
if (!do_frame) {
|
||||
continue;
|
||||
}
|
||||
float fade = audioFadeTracker(sample.pcm().data(), sample.pcm().size());
|
||||
shiftUp();
|
||||
// FASTLED_WARN("Audio sample size: " << sample.pcm().size());
|
||||
soundLevelMeter.processBlock(sample.pcm());
|
||||
// FASTLED_WARN("")
|
||||
auto dbfs = soundLevelMeter.getDBFS();
|
||||
FASTLED_UNUSED(dbfs);
|
||||
// FASTLED_WARN("getDBFS: " << dbfs);
|
||||
int32_t max = 0;
|
||||
for (size_t i = 0; i < sample.pcm().size(); ++i) {
|
||||
int32_t x = ABS(sample.pcm()[i]);
|
||||
if (x > max) {
|
||||
max = x;
|
||||
}
|
||||
}
|
||||
float anim =
|
||||
fl::map_range<float, float>(max, 0.0f, 32768.0f, 0.0f, 1.0f);
|
||||
anim = fl::clamp(anim, 0.0f, 1.0f);
|
||||
|
||||
x = fl::map_range<float, float>(anim, 0.0f, 1.0f, 0.0f, WIDTH - 1);
|
||||
// FASTLED_WARN("x: " << x);
|
||||
|
||||
// fft.run(sample.pcm(), &fftOut);
|
||||
sample.fft(&fftOut);
|
||||
|
||||
// FASTLED_ASSERT(fftOut.bins_raw.size() == WIDTH_2X,
|
||||
// "FFT bins size mismatch");
|
||||
|
||||
if (enableFFT) {
|
||||
auto max_x = fftOut.bins_raw.size() - 1;
|
||||
FASTLED_UNUSED(max_x);
|
||||
for (size_t i = 0; i < fftOut.bins_raw.size(); ++i) {
|
||||
auto x = i;
|
||||
auto v = fftOut.bins_db[i];
|
||||
// Map audio intensity to a position in the heat palette (0-255)
|
||||
v = fl::map_range<float, float>(v, 45, 70, 0, 1.f);
|
||||
v = fl::clamp(v, 0.0f, 1.0f);
|
||||
uint8_t heatIndex =
|
||||
fl::map_range<float, uint8_t>(v, 0, 1, 0, 255);
|
||||
|
||||
// FASTLED_WARN(v);
|
||||
|
||||
// Use FastLED's built-in HeatColors palette
|
||||
auto c = ColorFromPalette(HeatColors_p, heatIndex);
|
||||
c.fadeToBlackBy(255 - heatIndex);
|
||||
framebuffer[frameBufferXY(x, 0)] = c;
|
||||
// FASTLED_WARN("y: " << i << " b: " << b);
|
||||
}
|
||||
}
|
||||
|
||||
if (enableVolumeVis) {
|
||||
framebuffer[frameBufferXY(x, HEIGHT / 2)] = CRGB(0, 255, 0);
|
||||
}
|
||||
|
||||
if (enableRMS) {
|
||||
float rms = sample.rms();
|
||||
FASTLED_WARN("RMS: " << rms);
|
||||
rms = fl::map_range<float, float>(rms, 0.0f, 32768.0f, 0.0f, 1.0f);
|
||||
rms = fl::clamp(rms, 0.0f, 1.0f) * WIDTH;
|
||||
framebuffer[frameBufferXY(rms, HEIGHT * 3 / 4)] = CRGB(0, 0, 255);
|
||||
}
|
||||
if (true) {
|
||||
uint16_t fade_width = fade * (WIDTH - 1);
|
||||
uint16_t h = HEIGHT / 4;
|
||||
// yellow
|
||||
int index = frameBufferXY(fade_width, h);
|
||||
auto c = CRGB(255, 255, 0);
|
||||
framebuffer[index] = c;
|
||||
}
|
||||
}
|
||||
|
||||
// now downscale the framebuffer to the led matrix
|
||||
downscale(framebuffer, frameBufferXY, leds, ledsXY);
|
||||
|
||||
FastLED.show();
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
// I2S Audio Example for ESP32
|
||||
// This example demonstrates using I2S audio input to control FastLED strips
|
||||
// Based on audio levels from microphone or line input
|
||||
//
|
||||
// This example uses the extremely popular (as of 2025-September) INMP441 microphone.
|
||||
// Notes:
|
||||
// - Connect L/R to PWR so it's recognized as a right channel microphone.
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <FastLED.h>
|
||||
|
||||
#include "fl/sstream.h"
|
||||
#include "fl/type_traits.h"
|
||||
|
||||
#include "fl/audio_input.h"
|
||||
|
||||
#include "platforms/esp/32/audio/sound_util.h"
|
||||
|
||||
using fl::i16;
|
||||
|
||||
// I2S Configuration
|
||||
#define I2S_WS_PIN 7 // Word Select (LRCLK)
|
||||
#define I2S_SD_PIN 8 // Serial Data (DIN)
|
||||
#define I2S_CLK_PIN 4 // Serial Clock (BCLK)
|
||||
#define I2S_CHANNEL fl::Right
|
||||
|
||||
fl::AudioConfig config = fl::AudioConfig::CreateInmp441(I2S_WS_PIN, I2S_SD_PIN, I2S_CLK_PIN, I2S_CHANNEL);
|
||||
fl::shared_ptr<fl::IAudioInput> audioSource;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
Serial.println("I2S Audio FastLED Example");
|
||||
Serial.println("Waiting 5000ms for audio device to stdout initialization...");
|
||||
delay(5000);
|
||||
|
||||
// Initialize I2S Audio
|
||||
fl::string errorMsg;
|
||||
audioSource = fl::IAudioInput::create(config, &errorMsg);
|
||||
|
||||
if (!audioSource) {
|
||||
Serial.print("Failed to create audio source: ");
|
||||
Serial.println(errorMsg.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Start audio capture
|
||||
Serial.println("Starting audio capture...");
|
||||
audioSource->start();
|
||||
|
||||
// Check for start errors
|
||||
fl::string startErrorMsg;
|
||||
if (audioSource->error(&startErrorMsg)) {
|
||||
Serial.print("Audio start error: ");
|
||||
Serial.println(startErrorMsg.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.println("Audio capture started!");
|
||||
}
|
||||
|
||||
|
||||
void loop() {
|
||||
EVERY_N_MILLIS(1000) { Serial.println("loop active."); }
|
||||
// Check if audio source is valid
|
||||
if (!audioSource) {
|
||||
Serial.println("Audio source is null!");
|
||||
delay(1000);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for audio errors
|
||||
fl::string errorMsg;
|
||||
if (audioSource->error(&errorMsg)) {
|
||||
Serial.print("Audio error: ");
|
||||
Serial.println(errorMsg.c_str());
|
||||
delay(100);
|
||||
return;
|
||||
}
|
||||
|
||||
// Read audio data
|
||||
fl::AudioSample sample = audioSource->read();
|
||||
|
||||
if (sample.isValid()) {
|
||||
EVERY_N_MILLIS(100) {
|
||||
const auto& audioBuffer = sample.pcm();
|
||||
const i16* max_sample = fl::max_element(audioBuffer.begin(), audioBuffer.end());
|
||||
const i16* min_sample = fl::min_element(audioBuffer.begin(), audioBuffer.end());
|
||||
fl::sstream ss;
|
||||
ss << "\nRead " << audioBuffer.size() << " samples, timestamp: " << sample.timestamp() << "ms\n";
|
||||
ss << "Max sample: " << *max_sample << "\n";
|
||||
ss << "Min sample: " << *min_sample << "\n";
|
||||
ss << "RMS: " << sample.rms() << "\n";
|
||||
ss << "ZCF: " << sample.zcf() << "\n";
|
||||
FL_WARN(ss.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// I2S Audio Example (for ESP32 as of 2025-September)
|
||||
// This example demonstrates using I2S audio input to control FastLED strips
|
||||
// Based on audio levels from microphone or line input
|
||||
|
||||
#include "fl/audio_input.h"
|
||||
|
||||
#if FASTLED_HAS_AUDIO_INPUT
|
||||
#include "./AudioInput.h"
|
||||
#else
|
||||
#include "platforms/sketch_fake.hpp"
|
||||
#endif // FASTLED_HAS_AUDIO_INPUT
|
||||
83
.pio/libdeps/esp01_1m/FastLED/examples/Blink/Blink.ino
Normal file
83
.pio/libdeps/esp01_1m/FastLED/examples/Blink/Blink.ino
Normal file
@@ -0,0 +1,83 @@
|
||||
/// @file Blink.ino
|
||||
/// @brief Blink the first LED of an LED strip
|
||||
/// @example Blink.ino
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <FastLED.h>
|
||||
|
||||
// How many leds in your strip?
|
||||
#define NUM_LEDS 1
|
||||
|
||||
// For led chips like WS2812, which have a data line, ground, and power, you just
|
||||
// need to define DATA_PIN. For led chipsets that are SPI based (four wires - data, clock,
|
||||
// ground, and power), like the LPD8806 define both DATA_PIN and CLOCK_PIN
|
||||
// Clock pin only needed for SPI based chipsets when not using hardware SPI
|
||||
#define DATA_PIN 3
|
||||
#define CLOCK_PIN 13
|
||||
|
||||
// Define the array of leds
|
||||
CRGB leds[NUM_LEDS];
|
||||
|
||||
void setup() {
|
||||
//Serial.begin(9600);
|
||||
//Serial.println("BLINK setup starting");
|
||||
|
||||
// Uncomment/edit one of the following lines for your leds arrangement.
|
||||
// ## Clockless types ##
|
||||
FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS); // GRB ordering is assumed
|
||||
|
||||
//Serial.println("BLINK setup complete");
|
||||
// FastLED.addLeds<SM16824E, DATA_PIN, RGB>(leds, NUM_LEDS); // RGB ordering (uses SM16824EController)
|
||||
// FastLED.addLeds<SM16703, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<TM1829, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<TM1812, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<TM1809, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<TM1804, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<TM1803, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<UCS1903, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<UCS1903B, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<UCS1904, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<UCS2903, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<WS2812, DATA_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
|
||||
// FastLED.addLeds<WS2852, DATA_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
|
||||
// FastLED.addLeds<WS2812B, DATA_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
|
||||
// FastLED.addLeds<GS1903, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<SK6812, DATA_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
|
||||
// FastLED.addLeds<SK6822, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<APA106, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<PL9823, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<SK6822, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<WS2813, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<APA104, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<WS2811_400, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<GE8822, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<GW6205, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<GW6205_400, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<LPD1886, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<LPD1886_8BIT, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// ## Clocked (SPI) types ##
|
||||
// FastLED.addLeds<LPD6803, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
|
||||
// FastLED.addLeds<LPD8806, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
|
||||
// FastLED.addLeds<WS2801, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<WS2803, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<SM16716, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<P9813, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS); // BGR ordering is typical
|
||||
// FastLED.addLeds<DOTSTAR, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS); // BGR ordering is typical
|
||||
// FastLED.addLeds<APA102, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS); // BGR ordering is typical
|
||||
// FastLED.addLeds<SK9822, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS); // BGR ordering is typical
|
||||
}
|
||||
|
||||
void loop() {
|
||||
//Serial.println("BLINK");
|
||||
|
||||
// Turn the LED on, then pause
|
||||
leds[0] = CRGB::Red;
|
||||
FastLED.show();
|
||||
delay(500);
|
||||
|
||||
// Now turn the LED off, then pause
|
||||
leds[0] = CRGB::Black;
|
||||
FastLED.show();
|
||||
delay(500);
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
|
||||
/// @file BlinkParallel.ino
|
||||
/// @brief Shows parallel usage of WS2812 strips. Blinks once for red, twice for green, thrice for blue.
|
||||
/// @example BlinkParallel.ino
|
||||
|
||||
#include "FastLED.h"
|
||||
|
||||
|
||||
#if SKETCH_HAS_LOTS_OF_MEMORY
|
||||
// How many leds in your strip?
|
||||
#define NUM_LEDS 256
|
||||
#else
|
||||
#define NUM_LEDS 16
|
||||
#endif
|
||||
|
||||
// Demo of driving multiple WS2812 strips on different pins
|
||||
|
||||
// Define the array of leds
|
||||
CRGB leds[NUM_LEDS]; // Yes, they all share a buffer.
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
//FastLED.addLeds<WS2812, 5>(leds, NUM_LEDS); // GRB ordering is assumed
|
||||
FastLED.addLeds<WS2812, 1>(leds, NUM_LEDS); // GRB ordering is assumed
|
||||
FastLED.addLeds<WS2812, 2>(leds, NUM_LEDS); // GRB ordering is assumed
|
||||
FastLED.addLeds<WS2812, 3>(leds, NUM_LEDS); // GRB ordering is assumed
|
||||
FastLED.addLeds<WS2812, 4>(leds, NUM_LEDS); // GRB ordering is assumed
|
||||
|
||||
FL_WARN("Initialized 4 LED strips with " << NUM_LEDS << " LEDs each");
|
||||
FL_WARN("Setup complete - starting blink animation");
|
||||
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
void fill(CRGB color) {
|
||||
for (int i = 0; i < NUM_LEDS; i++) {
|
||||
leds[i] = color;
|
||||
}
|
||||
}
|
||||
|
||||
void blink(CRGB color, int times) {
|
||||
for (int i = 0; i < times; i++) {
|
||||
fill(color);
|
||||
FastLED.show();
|
||||
delay(500);
|
||||
fill(CRGB::Black);
|
||||
FastLED.show();
|
||||
delay(500);
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
static int loopCount = 0;
|
||||
|
||||
EVERY_N_MILLISECONDS(1000) { // Every 1 second (faster for QEMU testing)
|
||||
loopCount++;
|
||||
FL_WARN("Starting loop iteration " << loopCount);
|
||||
|
||||
// Add completion marker after a few loops for QEMU testing
|
||||
if (loopCount >= 2) { // Complete after 2 iterations instead of 3
|
||||
FL_WARN("FL_WARN test finished - completed " << loopCount << " iterations");
|
||||
}
|
||||
}
|
||||
|
||||
// Turn the LED on, then pause
|
||||
blink(CRGB(8,0,0), 1); // blink once for red
|
||||
blink(CRGB(0,8,0), 2); // blink twice for green
|
||||
blink(CRGB(0,0,8), 3); // blink thrice for blue
|
||||
|
||||
delay(50);
|
||||
|
||||
// now benchmark
|
||||
uint32_t start = millis();
|
||||
fill(CRGB(8,8,8));
|
||||
FastLED.show();
|
||||
uint32_t diff = millis() - start;
|
||||
|
||||
Serial.print("Time to fill and show for non blocking (ms): ");
|
||||
Serial.println(diff);
|
||||
|
||||
EVERY_N_MILLISECONDS(500) { // Every 0.5 seconds (faster for QEMU testing)
|
||||
FL_WARN("FastLED.show() timing: " << diff << "ms");
|
||||
}
|
||||
|
||||
delay(50);
|
||||
|
||||
start = millis();
|
||||
fill(CRGB(8,8,8));
|
||||
FastLED.show();
|
||||
FastLED.show();
|
||||
|
||||
diff = millis() - start;
|
||||
Serial.print("Time to fill and show for 2nd blocking (ms): ");
|
||||
Serial.println(diff);
|
||||
}
|
||||
32
.pio/libdeps/esp01_1m/FastLED/examples/Blur/Blur.ino
Normal file
32
.pio/libdeps/esp01_1m/FastLED/examples/Blur/Blur.ino
Normal file
@@ -0,0 +1,32 @@
|
||||
// UIDescription: This example shows how to blur a strip of LEDs. It uses the blur1d function to blur the strip and fadeToBlackBy to dim the strip. A bright pixel moves along the strip.
|
||||
// Author: Zach Vorhies
|
||||
|
||||
#include <FastLED.h>
|
||||
|
||||
#define NUM_LEDS 64
|
||||
#define DATA_PIN 3 // Change this to match your LED strip's data pin
|
||||
#define BRIGHTNESS 255
|
||||
|
||||
CRGB leds[NUM_LEDS];
|
||||
uint8_t pos = 0;
|
||||
bool toggle = false;
|
||||
|
||||
void setup() {
|
||||
FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
|
||||
FastLED.setBrightness(BRIGHTNESS);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Add a bright pixel that moves
|
||||
leds[pos] = CHSV(pos * 2, 255, 255);
|
||||
// Blur the entire strip
|
||||
blur1d(leds, NUM_LEDS, 172);
|
||||
fadeToBlackBy(leds, NUM_LEDS, 16);
|
||||
FastLED.show();
|
||||
// Move the position of the dot
|
||||
if (toggle) {
|
||||
pos = (pos + 1) % NUM_LEDS;
|
||||
}
|
||||
toggle = !toggle;
|
||||
delay(20);
|
||||
}
|
||||
65
.pio/libdeps/esp01_1m/FastLED/examples/Blur2d/Blur2d.ino
Normal file
65
.pio/libdeps/esp01_1m/FastLED/examples/Blur2d/Blur2d.ino
Normal file
@@ -0,0 +1,65 @@
|
||||
/// @file Blur2d.ino
|
||||
/// @brief Demonstrates 2D blur effects on LED matrix
|
||||
/// @example Blur2d.ino
|
||||
///
|
||||
/// 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`
|
||||
/// 4. When the compiler is done a web page will open.
|
||||
|
||||
// UIDescription: This example shows how to blur a strip of LEDs in 2d.
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <FastLED.h>
|
||||
|
||||
#include "fl/ui.h"
|
||||
#include "fl/xymap.h"
|
||||
|
||||
|
||||
using namespace fl;
|
||||
|
||||
|
||||
#if SKETCH_HAS_LOTS_OF_MEMORY
|
||||
#define WIDTH 22
|
||||
#define HEIGHT 22
|
||||
#else
|
||||
#define WIDTH 12
|
||||
#define HEIGHT 12
|
||||
#endif
|
||||
|
||||
#define NUM_LEDS (WIDTH * HEIGHT)
|
||||
#define BLUR_AMOUNT 172
|
||||
#define DATA_PIN 2 // Change this to match your LED strip's data pin
|
||||
#define BRIGHTNESS 255
|
||||
#define SERPENTINE true
|
||||
|
||||
CRGB leds[NUM_LEDS];
|
||||
uint8_t pos = 0;
|
||||
bool toggle = false;
|
||||
XYMap xymap(WIDTH, HEIGHT, SERPENTINE);
|
||||
|
||||
void setup() {
|
||||
FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS)
|
||||
.setScreenMap(xymap); // Necessary when using the FastLED web compiler to display properly on a web page.
|
||||
FastLED.setBrightness(BRIGHTNESS);
|
||||
Serial.println("setup");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
static int x = random(WIDTH);
|
||||
static int y = random(HEIGHT);
|
||||
static CRGB c = CRGB(0, 0, 0);
|
||||
blur2d(leds, WIDTH, HEIGHT, BLUR_AMOUNT, xymap);
|
||||
EVERY_N_MILLISECONDS(1000) {
|
||||
x = random(WIDTH);
|
||||
y = random(HEIGHT);
|
||||
uint8_t r = random(255);
|
||||
uint8_t g = random(255);
|
||||
uint8_t b = random(255);
|
||||
c = CRGB(r, g, b);
|
||||
}
|
||||
leds[xymap(x, y)] = c;
|
||||
FastLED.show();
|
||||
delay(20);
|
||||
}
|
||||
607
.pio/libdeps/esp01_1m/FastLED/examples/Chromancer/Chromancer.ino
Normal file
607
.pio/libdeps/esp01_1m/FastLED/examples/Chromancer/Chromancer.ino
Normal file
@@ -0,0 +1,607 @@
|
||||
/// @file Chromancer.ino
|
||||
/// @brief Hexagonal LED display visualization
|
||||
/// @example Chromancer.ino
|
||||
///
|
||||
/// 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`
|
||||
/// 4. When the compiler is done a web page will open.
|
||||
|
||||
/*
|
||||
Original Source: https://github.com/ZackFreedman/Chromance
|
||||
GaryWoo's Video: https://www.youtube.com/watch?v=-nSCtxa2Kp0
|
||||
GaryWoo's LedMap: https://gist.github.com/Garywoo/b6cd1ea90cb5e17cc60b01ae68a2b770
|
||||
GaryWoo's presets: https://gist.github.com/Garywoo/82fa67c6e1f9529dc16a01dd97d05d58
|
||||
Chromance wall hexagon source (emotion controlled w/ EmotiBit)
|
||||
Partially cribbed from the DotStar example
|
||||
I smooshed in the ESP32 BasicOTA sketch, too
|
||||
|
||||
(C) Voidstar Lab 2021
|
||||
*/
|
||||
|
||||
#include "fl/sketch_macros.h"
|
||||
#include "fl/warn.h"
|
||||
|
||||
#if !SKETCH_HAS_LOTS_OF_MEMORY
|
||||
// Platform does not have enough memory
|
||||
// Other platforms have weird issues. Will revisit this later.
|
||||
#include <Arduino.h>
|
||||
|
||||
void setup() {
|
||||
// Use Serial.println instead of FL_WARN to prevent optimization away
|
||||
Serial.begin(115200);
|
||||
Serial.println("Chromancer.ino: setup() - Platform has insufficient memory for full demo");
|
||||
}
|
||||
void loop() {
|
||||
// Use Serial.println instead of FL_WARN to prevent optimization away
|
||||
Serial.println("Chromancer.ino: loop() - Platform has insufficient memory for full demo");
|
||||
delay(1000); // Prevent rapid printing
|
||||
}
|
||||
#else
|
||||
|
||||
|
||||
#include <FastLED.h>
|
||||
|
||||
#include "fl/screenmap.h"
|
||||
#include "fl/math_macros.h"
|
||||
#include "fl/json.h"
|
||||
#include "fl/ui.h"
|
||||
#include "fl/map.h"
|
||||
|
||||
#include "fl/str.h"
|
||||
|
||||
#include "./screenmap.json.h"
|
||||
#include "./mapping.h"
|
||||
#include "./ripple.h"
|
||||
#include "./detail.h"
|
||||
|
||||
using namespace fl;
|
||||
|
||||
enum {
|
||||
BlackStrip = 0,
|
||||
GreenStrip = 1,
|
||||
RedStrip = 2,
|
||||
BlueStrip = 3,
|
||||
};
|
||||
|
||||
|
||||
// Strips are different lengths because I am a dumb
|
||||
|
||||
constexpr int lengths[] = {
|
||||
154, // Black strip
|
||||
168, // Green strip
|
||||
84, // Red strip
|
||||
154 // Blue strip
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// non emscripten uses separate arrays for each strip. Eventually emscripten
|
||||
// should support this as well but right now we don't
|
||||
CRGB leds0[lengths[BlackStrip]] = {};
|
||||
CRGB leds1[lengths[GreenStrip]] = {};
|
||||
CRGB leds2[lengths[RedStrip]] = {}; // Red
|
||||
CRGB leds3[lengths[BlueStrip]] = {};
|
||||
CRGB *leds[] = {leds0, leds1, leds2, leds3};
|
||||
|
||||
|
||||
byte ledColors[40][14][3]; // LED buffer - each ripple writes to this, then we
|
||||
// write this to the strips
|
||||
//float decay = 0.97; // Multiply all LED's by this amount each tick to create
|
||||
// fancy fading tails
|
||||
|
||||
UISlider sliderDecay("decay", .97f, .8, 1.0, .01);
|
||||
|
||||
// These ripples are endlessly reused so we don't need to do any memory
|
||||
// management
|
||||
#define numberOfRipples 30
|
||||
Ripple ripples[numberOfRipples] = {
|
||||
Ripple(0), Ripple(1), Ripple(2), Ripple(3), Ripple(4), Ripple(5),
|
||||
Ripple(6), Ripple(7), Ripple(8), Ripple(9), Ripple(10), Ripple(11),
|
||||
Ripple(12), Ripple(13), Ripple(14), Ripple(15), Ripple(16), Ripple(17),
|
||||
Ripple(18), Ripple(19), Ripple(20), Ripple(21), Ripple(22), Ripple(23),
|
||||
Ripple(24), Ripple(25), Ripple(26), Ripple(27), Ripple(28), Ripple(29),
|
||||
};
|
||||
|
||||
// Biometric detection and interpretation
|
||||
// IR (heartbeat) is used to fire outward ripples
|
||||
float lastIrReading; // When our heart pumps, reflected IR drops sharply
|
||||
float highestIrReading; // These vars let us detect this drop
|
||||
unsigned long
|
||||
lastHeartbeat; // Track last heartbeat so we can detect noise/disconnections
|
||||
#define heartbeatLockout \
|
||||
500 // Heartbeats that happen within this many milliseconds are ignored
|
||||
#define heartbeatDelta 300 // Drop in reflected IR that constitutes a heartbeat
|
||||
|
||||
// Heartbeat color ripples are proportional to skin temperature
|
||||
#define lowTemperature 33.0 // Resting temperature
|
||||
#define highTemperature 37.0 // Really fired up
|
||||
float lastKnownTemperature =
|
||||
(lowTemperature + highTemperature) /
|
||||
2.0; // Carries skin temperature from temperature callback to IR callback
|
||||
|
||||
// EDA code was too unreliable and was cut.
|
||||
// TODO: Rebuild EDA code
|
||||
|
||||
// Gyroscope is used to reject data if you're moving too much
|
||||
#define gyroAlpha 0.9 // Exponential smoothing constant
|
||||
#define gyroThreshold \
|
||||
300 // Minimum angular velocity total (X+Y+Z) that disqualifies readings
|
||||
float gyroX, gyroY, gyroZ;
|
||||
|
||||
// If you don't have an EmotiBit or don't feel like wearing it, that's OK
|
||||
// We'll fire automatic pulses
|
||||
#define randomPulsesEnabled true // Fire random rainbow pulses from random nodes
|
||||
#define cubePulsesEnabled true // Draw cubes at random nodes
|
||||
UICheckbox starburstPulsesEnabled("Starburst Pulses", true);
|
||||
UICheckbox simulatedBiometricsEnabled("Simulated Biometrics", true);
|
||||
|
||||
#define autoPulseTimeout \
|
||||
5000 // If no heartbeat is received in this many ms, begin firing
|
||||
// random/simulated pulses
|
||||
#define randomPulseTime 2000 // Fire a random pulse every (this many) ms
|
||||
unsigned long lastRandomPulse;
|
||||
byte lastAutoPulseNode = 255;
|
||||
|
||||
byte numberOfAutoPulseTypes =
|
||||
randomPulsesEnabled + cubePulsesEnabled + int(starburstPulsesEnabled);
|
||||
byte currentAutoPulseType = 255;
|
||||
#define autoPulseChangeTime 30000
|
||||
unsigned long lastAutoPulseChange;
|
||||
|
||||
#define simulatedHeartbeatBaseTime \
|
||||
600 // Fire a simulated heartbeat pulse after at least this many ms
|
||||
#define simulatedHeartbeatVariance \
|
||||
200 // Add random jitter to simulated heartbeat
|
||||
#define simulatedEdaBaseTime 1000 // Same, but for inward EDA pulses
|
||||
#define simulatedEdaVariance 10000
|
||||
unsigned long nextSimulatedHeartbeat;
|
||||
unsigned long nextSimulatedEda;
|
||||
|
||||
// Helper function to check if a node is on the border
|
||||
bool isNodeOnBorder(byte node) {
|
||||
for (int i = 0; i < numberOfBorderNodes; i++) {
|
||||
if (node == borderNodes[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
UITitle title("Chromancer");
|
||||
UIDescription description("Take 6 seconds to boot up. Chromancer is a wall-mounted hexagonal LED display that originally reacted to biometric data from an EmotiBit sensor. It visualizes your heartbeat, skin temperature, and movement in real-time. Chromancer also has a few built-in effects that can be triggered with the push of a button. Enjoy!");
|
||||
UICheckbox allWhite("All White", false);
|
||||
|
||||
UIButton simulatedHeartbeat("Simulated Heartbeat");
|
||||
UIButton triggerStarburst("Trigger Starburst");
|
||||
UIButton triggerRainbowCube("Rainbow Cube");
|
||||
UIButton triggerBorderWave("Border Wave");
|
||||
UIButton triggerSpiral("Spiral Wave");
|
||||
bool wasHeartbeatClicked = false;
|
||||
bool wasStarburstClicked = false;
|
||||
bool wasRainbowCubeClicked = false;
|
||||
bool wasBorderWaveClicked = false;
|
||||
bool wasSpiralClicked = false;
|
||||
|
||||
// Group related UI elements using UIGroup template multi-argument constructor
|
||||
UIGroup effectTriggers("Effect Triggers", simulatedHeartbeat, triggerStarburst, triggerRainbowCube, triggerBorderWave, triggerSpiral);
|
||||
UIGroup automationControls("Automation", starburstPulsesEnabled, simulatedBiometricsEnabled);
|
||||
UIGroup displayControls("Display", sliderDecay, allWhite);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
Serial.println("*** LET'S GOOOOO ***");
|
||||
|
||||
Serial.println("JSON SCREENMAP");
|
||||
Serial.println(JSON_SCREEN_MAP);
|
||||
|
||||
fl::fl_map<fl::string, ScreenMap> segmentMaps;
|
||||
ScreenMap::ParseJson(JSON_SCREEN_MAP, &segmentMaps);
|
||||
|
||||
printf("Parsed %d segment maps\n", int(segmentMaps.size()));
|
||||
for (auto kv : segmentMaps) {
|
||||
Serial.print(kv.first.c_str());
|
||||
Serial.print(" ");
|
||||
Serial.println(kv.second.getLength());
|
||||
}
|
||||
|
||||
|
||||
// ScreenMap screenmaps[4];
|
||||
ScreenMap red, black, green, blue;
|
||||
bool ok = true;
|
||||
|
||||
auto red_it = segmentMaps.find("red_segment");
|
||||
ok = (red_it != segmentMaps.end()) && ok;
|
||||
if (red_it != segmentMaps.end()) red = red_it->second;
|
||||
|
||||
auto black_it = segmentMaps.find("back_segment");
|
||||
ok = (black_it != segmentMaps.end()) && ok;
|
||||
if (black_it != segmentMaps.end()) black = black_it->second;
|
||||
|
||||
auto green_it = segmentMaps.find("green_segment");
|
||||
ok = (green_it != segmentMaps.end()) && ok;
|
||||
if (green_it != segmentMaps.end()) green = green_it->second;
|
||||
|
||||
auto blue_it = segmentMaps.find("blue_segment");
|
||||
ok = (blue_it != segmentMaps.end()) && ok;
|
||||
if (blue_it != segmentMaps.end()) blue = blue_it->second;
|
||||
if (!ok) {
|
||||
Serial.println("Failed to get all segment maps");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
CRGB* red_leds = leds[RedStrip];
|
||||
CRGB* black_leds = leds[BlackStrip];
|
||||
CRGB* green_leds = leds[GreenStrip];
|
||||
CRGB* blue_leds = leds[BlueStrip];
|
||||
|
||||
FastLED.addLeds<WS2812, 2>(black_leds, lengths[BlackStrip]).setScreenMap(black);
|
||||
FastLED.addLeds<WS2812, 3>(green_leds, lengths[GreenStrip]).setScreenMap(green);
|
||||
FastLED.addLeds<WS2812, 1>(red_leds, lengths[RedStrip]).setScreenMap(red);
|
||||
FastLED.addLeds<WS2812, 4>(blue_leds, lengths[BlueStrip]).setScreenMap(blue);
|
||||
|
||||
FastLED.show();
|
||||
}
|
||||
|
||||
|
||||
void loop() {
|
||||
unsigned long benchmark = millis();
|
||||
FL_UNUSED(benchmark);
|
||||
|
||||
// Fade all dots to create trails
|
||||
for (int strip = 0; strip < 40; strip++) {
|
||||
for (int led = 0; led < 14; led++) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
ledColors[strip][led][i] *= sliderDecay.value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < numberOfRipples; i++) {
|
||||
ripples[i].advance(ledColors);
|
||||
}
|
||||
|
||||
for (int segment = 0; segment < 40; segment++) {
|
||||
for (int fromBottom = 0; fromBottom < 14; fromBottom++) {
|
||||
int strip = ledAssignments[segment][0];
|
||||
int led = round(fmap(fromBottom, 0, 13, ledAssignments[segment][2],
|
||||
ledAssignments[segment][1]));
|
||||
leds[strip][led] = CRGB(ledColors[segment][fromBottom][0],
|
||||
ledColors[segment][fromBottom][1],
|
||||
ledColors[segment][fromBottom][2]);
|
||||
}
|
||||
}
|
||||
|
||||
if (allWhite) {
|
||||
// for all strips
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < lengths[i]; j++) {
|
||||
leds[i][j] = CRGB::White;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FastLED.show();
|
||||
|
||||
|
||||
// Check if buttons were clicked
|
||||
wasHeartbeatClicked = bool(simulatedHeartbeat);
|
||||
wasStarburstClicked = bool(triggerStarburst);
|
||||
wasRainbowCubeClicked = bool(triggerRainbowCube);
|
||||
wasBorderWaveClicked = bool(triggerBorderWave);
|
||||
wasSpiralClicked = bool(triggerSpiral);
|
||||
|
||||
if (wasSpiralClicked) {
|
||||
// Trigger spiral wave effect from center
|
||||
unsigned int baseColor = random(0xFFFF);
|
||||
byte centerNode = 15; // Center node
|
||||
|
||||
// Create 6 ripples in a spiral pattern
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (nodeConnections[centerNode][i] >= 0) {
|
||||
for (int j = 0; j < numberOfRipples; j++) {
|
||||
if (ripples[j].state == dead) {
|
||||
ripples[j].start(
|
||||
centerNode, i,
|
||||
Adafruit_DotStar_ColorHSV(
|
||||
baseColor + (0xFFFF / 6) * i, 255, 255),
|
||||
0.3 + (i * 0.1), // Varying speeds creates spiral effect
|
||||
2000,
|
||||
i % 2 ? alwaysTurnsLeft : alwaysTurnsRight); // Alternating turn directions
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
lastHeartbeat = millis();
|
||||
}
|
||||
|
||||
if (wasBorderWaveClicked) {
|
||||
// Trigger immediate border wave effect
|
||||
unsigned int baseColor = random(0xFFFF);
|
||||
|
||||
// Start ripples from each border node in sequence
|
||||
for (int i = 0; i < numberOfBorderNodes; i++) {
|
||||
byte node = borderNodes[i];
|
||||
// Find an inward direction
|
||||
for (int dir = 0; dir < 6; dir++) {
|
||||
if (nodeConnections[node][dir] >= 0 &&
|
||||
!isNodeOnBorder(nodeConnections[node][dir])) {
|
||||
for (int j = 0; j < numberOfRipples; j++) {
|
||||
if (ripples[j].state == dead) {
|
||||
ripples[j].start(
|
||||
node, dir,
|
||||
Adafruit_DotStar_ColorHSV(
|
||||
baseColor + (0xFFFF / numberOfBorderNodes) * i,
|
||||
255, 255),
|
||||
.4, 2000, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
lastHeartbeat = millis();
|
||||
}
|
||||
|
||||
if (wasRainbowCubeClicked) {
|
||||
// Trigger immediate rainbow cube effect
|
||||
int node = cubeNodes[random(numberOfCubeNodes)];
|
||||
unsigned int baseColor = random(0xFFFF);
|
||||
byte behavior = random(2) ? alwaysTurnsLeft : alwaysTurnsRight;
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (nodeConnections[node][i] >= 0) {
|
||||
for (int j = 0; j < numberOfRipples; j++) {
|
||||
if (ripples[j].state == dead) {
|
||||
ripples[j].start(
|
||||
node, i,
|
||||
Adafruit_DotStar_ColorHSV(
|
||||
baseColor + (0xFFFF / 6) * i, 255, 255),
|
||||
.5, 2000, behavior);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
lastHeartbeat = millis();
|
||||
}
|
||||
|
||||
if (wasStarburstClicked) {
|
||||
// Trigger immediate starburst effect
|
||||
unsigned int baseColor = random(0xFFFF);
|
||||
byte behavior = random(2) ? alwaysTurnsLeft : alwaysTurnsRight;
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
for (int j = 0; j < numberOfRipples; j++) {
|
||||
if (ripples[j].state == dead) {
|
||||
ripples[j].start(
|
||||
starburstNode, i,
|
||||
Adafruit_DotStar_ColorHSV(
|
||||
baseColor + (0xFFFF / 6) * i, 255, 255),
|
||||
.65, 1500, behavior);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
lastHeartbeat = millis();
|
||||
}
|
||||
|
||||
if (wasHeartbeatClicked) {
|
||||
// Trigger immediate heartbeat effect
|
||||
for (int i = 0; i < 6; i++) {
|
||||
for (int j = 0; j < numberOfRipples; j++) {
|
||||
if (ripples[j].state == dead) {
|
||||
ripples[j].start(15, i, 0xEE1111,
|
||||
float(random(100)) / 100.0 * .1 + .4, 1000, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
lastHeartbeat = millis();
|
||||
}
|
||||
|
||||
if (millis() - lastHeartbeat >= autoPulseTimeout) {
|
||||
// When biometric data is unavailable, visualize at random
|
||||
if (numberOfAutoPulseTypes &&
|
||||
millis() - lastRandomPulse >= randomPulseTime) {
|
||||
unsigned int baseColor = random(0xFFFF);
|
||||
|
||||
if (currentAutoPulseType == 255 ||
|
||||
(numberOfAutoPulseTypes > 1 &&
|
||||
millis() - lastAutoPulseChange >= autoPulseChangeTime)) {
|
||||
byte possiblePulse = 255;
|
||||
while (true) {
|
||||
possiblePulse = random(3);
|
||||
|
||||
if (possiblePulse == currentAutoPulseType)
|
||||
continue;
|
||||
|
||||
switch (possiblePulse) {
|
||||
case 0:
|
||||
if (!randomPulsesEnabled)
|
||||
continue;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (!cubePulsesEnabled)
|
||||
continue;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (!starburstPulsesEnabled)
|
||||
continue;
|
||||
break;
|
||||
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
currentAutoPulseType = possiblePulse;
|
||||
lastAutoPulseChange = millis();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (currentAutoPulseType) {
|
||||
case 0: {
|
||||
int node = 0;
|
||||
bool foundStartingNode = false;
|
||||
while (!foundStartingNode) {
|
||||
node = random(25);
|
||||
foundStartingNode = true;
|
||||
for (int i = 0; i < numberOfBorderNodes; i++) {
|
||||
// Don't fire a pulse on one of the outer nodes - it
|
||||
// looks boring
|
||||
if (node == borderNodes[i])
|
||||
foundStartingNode = false;
|
||||
}
|
||||
|
||||
if (node == lastAutoPulseNode)
|
||||
foundStartingNode = false;
|
||||
}
|
||||
|
||||
lastAutoPulseNode = node;
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (nodeConnections[node][i] >= 0) {
|
||||
for (int j = 0; j < numberOfRipples; j++) {
|
||||
if (ripples[j].state == dead) {
|
||||
ripples[j].start(
|
||||
node, i,
|
||||
// strip0.ColorHSV(baseColor
|
||||
// + (0xFFFF / 6) * i,
|
||||
// 255, 255),
|
||||
Adafruit_DotStar_ColorHSV(baseColor, 255,
|
||||
255),
|
||||
float(random(100)) / 100.0 * .2 + .5, 3000,
|
||||
1);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 1: {
|
||||
int node = cubeNodes[random(numberOfCubeNodes)];
|
||||
|
||||
while (node == lastAutoPulseNode)
|
||||
node = cubeNodes[random(numberOfCubeNodes)];
|
||||
|
||||
lastAutoPulseNode = node;
|
||||
|
||||
byte behavior = random(2) ? alwaysTurnsLeft : alwaysTurnsRight;
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (nodeConnections[node][i] >= 0) {
|
||||
for (int j = 0; j < numberOfRipples; j++) {
|
||||
if (ripples[j].state == dead) {
|
||||
ripples[j].start(
|
||||
node, i,
|
||||
// strip0.ColorHSV(baseColor
|
||||
// + (0xFFFF / 6) * i,
|
||||
// 255, 255),
|
||||
Adafruit_DotStar_ColorHSV(baseColor, 255,
|
||||
255),
|
||||
.5, 2000, behavior);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 2: {
|
||||
byte behavior = random(2) ? alwaysTurnsLeft : alwaysTurnsRight;
|
||||
|
||||
lastAutoPulseNode = starburstNode;
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
for (int j = 0; j < numberOfRipples; j++) {
|
||||
if (ripples[j].state == dead) {
|
||||
ripples[j].start(
|
||||
starburstNode, i,
|
||||
Adafruit_DotStar_ColorHSV(
|
||||
baseColor + (0xFFFF / 6) * i, 255, 255),
|
||||
.65, 1500, behavior);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
lastRandomPulse = millis();
|
||||
}
|
||||
|
||||
if (simulatedBiometricsEnabled) {
|
||||
// Simulated heartbeat
|
||||
if (millis() >= nextSimulatedHeartbeat) {
|
||||
for (int i = 0; i < 6; i++) {
|
||||
for (int j = 0; j < numberOfRipples; j++) {
|
||||
if (ripples[j].state == dead) {
|
||||
ripples[j].start(
|
||||
15, i, 0xEE1111,
|
||||
float(random(100)) / 100.0 * .1 + .4, 1000, 0);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nextSimulatedHeartbeat = millis() + simulatedHeartbeatBaseTime +
|
||||
random(simulatedHeartbeatVariance);
|
||||
}
|
||||
|
||||
// Simulated EDA ripples
|
||||
if (millis() >= nextSimulatedEda) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int j = 0; j < numberOfRipples; j++) {
|
||||
if (ripples[j].state == dead) {
|
||||
byte targetNode =
|
||||
borderNodes[random(numberOfBorderNodes)];
|
||||
byte direction = 255;
|
||||
|
||||
while (direction == 255) {
|
||||
direction = random(6);
|
||||
if (nodeConnections[targetNode][direction] < 0)
|
||||
direction = 255;
|
||||
}
|
||||
|
||||
ripples[j].start(
|
||||
targetNode, direction, 0x1111EE,
|
||||
float(random(100)) / 100.0 * .5 + 2, 300, 2);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nextSimulatedEda = millis() + simulatedEdaBaseTime +
|
||||
random(simulatedEdaVariance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Serial.print("Benchmark: ");
|
||||
// Serial.println(millis() - benchmark);
|
||||
}
|
||||
|
||||
#endif // __AVR__
|
||||
80
.pio/libdeps/esp01_1m/FastLED/examples/Chromancer/detail.h
Normal file
80
.pio/libdeps/esp01_1m/FastLED/examples/Chromancer/detail.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#include "fl/stdint.h"
|
||||
|
||||
|
||||
inline uint32_t Adafruit_DotStar_ColorHSV(uint16_t hue, uint8_t sat, uint8_t val) {
|
||||
|
||||
uint8_t r, g, b;
|
||||
|
||||
// Remap 0-65535 to 0-1529. Pure red is CENTERED on the 64K rollover;
|
||||
// 0 is not the start of pure red, but the midpoint...a few values above
|
||||
// zero and a few below 65536 all yield pure red (similarly, 32768 is the
|
||||
// midpoint, not start, of pure cyan). The 8-bit RGB hexcone (256 values
|
||||
// each for red, green, blue) really only allows for 1530 distinct hues
|
||||
// (not 1536, more on that below), but the full unsigned 16-bit type was
|
||||
// chosen for hue so that one's code can easily handle a contiguous color
|
||||
// wheel by allowing hue to roll over in either direction.
|
||||
hue = (hue * 1530L + 32768) / 65536;
|
||||
// Because red is centered on the rollover point (the +32768 above,
|
||||
// essentially a fixed-point +0.5), the above actually yields 0 to 1530,
|
||||
// where 0 and 1530 would yield the same thing. Rather than apply a
|
||||
// costly modulo operator, 1530 is handled as a special case below.
|
||||
|
||||
// So you'd think that the color "hexcone" (the thing that ramps from
|
||||
// pure red, to pure yellow, to pure green and so forth back to red,
|
||||
// yielding six slices), and with each color component having 256
|
||||
// possible values (0-255), might have 1536 possible items (6*256),
|
||||
// but in reality there's 1530. This is because the last element in
|
||||
// each 256-element slice is equal to the first element of the next
|
||||
// slice, and keeping those in there this would create small
|
||||
// discontinuities in the color wheel. So the last element of each
|
||||
// slice is dropped...we regard only elements 0-254, with item 255
|
||||
// being picked up as element 0 of the next slice. Like this:
|
||||
// Red to not-quite-pure-yellow is: 255, 0, 0 to 255, 254, 0
|
||||
// Pure yellow to not-quite-pure-green is: 255, 255, 0 to 1, 255, 0
|
||||
// Pure green to not-quite-pure-cyan is: 0, 255, 0 to 0, 255, 254
|
||||
// and so forth. Hence, 1530 distinct hues (0 to 1529), and hence why
|
||||
// the constants below are not the multiples of 256 you might expect.
|
||||
|
||||
// Convert hue to R,G,B (nested ifs faster than divide+mod+switch):
|
||||
if (hue < 510) { // Red to Green-1
|
||||
b = 0;
|
||||
if (hue < 255) { // Red to Yellow-1
|
||||
r = 255;
|
||||
g = hue; // g = 0 to 254
|
||||
} else { // Yellow to Green-1
|
||||
r = 510 - hue; // r = 255 to 1
|
||||
g = 255;
|
||||
}
|
||||
} else if (hue < 1020) { // Green to Blue-1
|
||||
r = 0;
|
||||
if (hue < 765) { // Green to Cyan-1
|
||||
g = 255;
|
||||
b = hue - 510; // b = 0 to 254
|
||||
} else { // Cyan to Blue-1
|
||||
g = 1020 - hue; // g = 255 to 1
|
||||
b = 255;
|
||||
}
|
||||
} else if (hue < 1530) { // Blue to Red-1
|
||||
g = 0;
|
||||
if (hue < 1275) { // Blue to Magenta-1
|
||||
r = hue - 1020; // r = 0 to 254
|
||||
b = 255;
|
||||
} else { // Magenta to Red-1
|
||||
r = 255;
|
||||
b = 1530 - hue; // b = 255 to 1
|
||||
}
|
||||
} else { // Last 0.5 Red (quicker than % operator)
|
||||
r = 255;
|
||||
g = b = 0;
|
||||
}
|
||||
|
||||
// Apply saturation and value to R,G,B, pack into 32-bit result:
|
||||
uint32_t v1 = 1 + val; // 1 to 256; allows >>8 instead of /255
|
||||
uint16_t s1 = 1 + sat; // 1 to 256; same reason
|
||||
uint8_t s2 = 255 - sat; // 255 to 0
|
||||
return ((((((r * s1) >> 8) + s2) * v1) & 0xff00) << 8) |
|
||||
(((((g * s1) >> 8) + s2) * v1) & 0xff00) |
|
||||
(((((b * s1) >> 8) + s2) * v1) >> 8);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
const char JSON_MAP[] = R"({"map": [
|
||||
|
||||
406,407,408,409,410,411,412,413,414,415,416,417,418,419,
|
||||
420,421,422,423,424,425,426,427,428,429,430,431,432,433,
|
||||
434,435,436,437,438,439,440,441,442,443,444,445,446,447,
|
||||
532,533,534,535,536,537,538,539,540,541,542,543,544,545,
|
||||
546,547,548,549,550,551,552,553,554,555,556,557,558,559,
|
||||
377,376,375,374,373,372,371,370,369,368,367,366,365,364,
|
||||
363,362,361,360,359,358,357,356,355,354,353,352,351,350,
|
||||
392,393,394,395,396,397,398,399,400,401,402,403,404,405,
|
||||
223,222,221,220,219,218,217,216,215,214,213,212,211,210,
|
||||
125,124,123,122,121,120,119,118,117,116,115,114,113,112,
|
||||
111,110,109,108,107,106,105,104,103,102,101,100,99,98,
|
||||
97,96,95,94,93,92,91,90,89,88,87,86,85,84,
|
||||
168,169,170,171,172,173,174,175,176,177,178,179,180,181,
|
||||
182,183,184,185,186,187,188,189,190,191,192,193,194,195,
|
||||
196,197,198,199,200,201,202,203,204,205,206,207,208,209,
|
||||
126,127,128,129,130,131,132,133,134,135,136,137,138,139,
|
||||
307,306,305,304,303,302,301,300,299,298,297,296,295,294,
|
||||
349,348,347,346,345,344,343,342,341,340,339,338,337,336,
|
||||
391,390,389,388,387,386,385,384,383,382,381,380,379,378,
|
||||
13,12,11,10,9,8,7,6,5,4,3,2,1,0,
|
||||
461,460,459,458,457,456,455,454,453,452,451,450,449,448,
|
||||
531,530,529,528,527,526,525,524,523,522,521,520,519,518,
|
||||
517,516,515,514,513,512,511,510,509,508,507,506,505,504,
|
||||
503,502,501,500,499,498,497,496,495,494,493,492,491,490,
|
||||
476,477,478,479,480,481,482,483,484,485,486,487,488,489,
|
||||
321,320,319,318,317,316,315,314,313,312,311,310,309,308,
|
||||
153,152,151,150,149,148,147,146,145,144,143,142,141,140,
|
||||
322,323,324,325,326,327,328,329,330,331,332,333,334,335,
|
||||
475,474,473,472,471,470,469,468,467,466,465,464,463,462,
|
||||
154,155,156,157,158,159,160,161,162,163,164,165,166,167,
|
||||
14,15,16,17,18,19,20,21,22,23,24,25,26,27,
|
||||
28,29,30,31,32,33,34,35,36,37,38,39,40,41,
|
||||
42,43,44,45,46,47,48,49,50,51,52,53,54,55,
|
||||
56,57,58,59,60,61,62,63,64,65,66,67,68,69,
|
||||
70,71,72,73,74,75,76,77,78,79,80,81,82,83,
|
||||
237,236,235,234,233,232,231,230,229,228,227,226,225,224,
|
||||
238,239,240,241,242,243,244,245,246,247,248,249,250,251,
|
||||
252,253,254,255,256,257,258,259,260,261,262,263,264,265,
|
||||
266,267,268,269,270,271,272,273,274,275,276,277,278,279,
|
||||
280,281,282,283,284,285,286,287,288,289,290,291,292,293
|
||||
]}
|
||||
)";
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
270
.pio/libdeps/esp01_1m/FastLED/examples/Chromancer/gen.py
Normal file
270
.pio/libdeps/esp01_1m/FastLED/examples/Chromancer/gen.py
Normal file
@@ -0,0 +1,270 @@
|
||||
"""
|
||||
Generates the hexegon using math.
|
||||
"""
|
||||
|
||||
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
import json
|
||||
|
||||
|
||||
from math import pi, cos, sin
|
||||
|
||||
LED_PER_STRIP = 14
|
||||
SPACE_PER_LED = 30.0 # Increased for better visibility
|
||||
LED_DIAMETER = SPACE_PER_LED / 4
|
||||
MIRROR_X = True # Diagramed from the reverse side. Reverse the x-axis
|
||||
|
||||
SMALLEST_ANGLE = 360 / 6
|
||||
|
||||
|
||||
class HexagonAngle(Enum):
|
||||
UP = 90
|
||||
DOWN = 270
|
||||
RIGHT_UP = 30
|
||||
RIGHT_DOWN = 360 - 30
|
||||
LEFT_UP = 150 # (RIGHT_DOWN + 180) % 360
|
||||
LEFT_DOWN = 210 # (RIGHT_UP + 180) % 360
|
||||
|
||||
|
||||
def toRads(angle: float) -> float:
|
||||
return angle * (pi / 180)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Point:
|
||||
x: float
|
||||
y: float
|
||||
|
||||
@staticmethod
|
||||
def toJson(points: list["Point"]) -> list[dict]:
|
||||
x_values = [p.x for p in points]
|
||||
y_values = [p.y for p in points]
|
||||
# round
|
||||
x_values = [round(x, 4) for x in x_values]
|
||||
y_values = [round(y, 4) for y in y_values]
|
||||
if MIRROR_X:
|
||||
x_values = [-x for x in x_values]
|
||||
|
||||
return {"x": x_values, "y": y_values, "diameter": LED_DIAMETER}
|
||||
|
||||
def copy(self) -> "Point":
|
||||
return Point(self.x, self.y)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
x_rounded = round(self.x, 2)
|
||||
y_rounded = round(self.y, 2)
|
||||
return f"({x_rounded}, {y_rounded})"
|
||||
|
||||
|
||||
def next_point(pos: Point, angle: HexagonAngle, space: float) -> Point:
|
||||
degrees = angle.value
|
||||
angle_rad = toRads(degrees)
|
||||
x = pos.x + space * cos(angle_rad)
|
||||
y = pos.y + space * sin(angle_rad)
|
||||
return Point(x, y)
|
||||
|
||||
|
||||
def gen_points(
|
||||
input: list[HexagonAngle], leds_per_strip: int, startPos: Point,
|
||||
exclude: list[int] | None = None,
|
||||
add_last: bool = False
|
||||
) -> list[Point]:
|
||||
points: list[Point] = []
|
||||
if (not input) or (not leds_per_strip):
|
||||
return points
|
||||
exclude = exclude or []
|
||||
# Start FSM. Start pointer get's put into the accumulator.
|
||||
curr_point: Point = Point(startPos.x, startPos.y)
|
||||
# points.append(curr_point)
|
||||
last_angle = input[0]
|
||||
for i,angle in enumerate(input):
|
||||
excluded = i in exclude
|
||||
values = list(range(leds_per_strip))
|
||||
last_angle = angle
|
||||
for v in values:
|
||||
last_angle = angle
|
||||
curr_point = next_point(curr_point, angle, SPACE_PER_LED)
|
||||
if not excluded:
|
||||
points.append(curr_point)
|
||||
#if i == len(input) - 1:
|
||||
# break
|
||||
# Next starting point
|
||||
curr_point = next_point(curr_point, last_angle, SPACE_PER_LED)
|
||||
#if not excluded:
|
||||
# points.append(curr_point)
|
||||
if add_last:
|
||||
points.append(curr_point)
|
||||
return points
|
||||
|
||||
|
||||
|
||||
def main() -> None:
|
||||
startPos = Point(0, 0)
|
||||
hexagon_angles = [
|
||||
HexagonAngle.UP,
|
||||
HexagonAngle.RIGHT_UP,
|
||||
HexagonAngle.RIGHT_DOWN,
|
||||
HexagonAngle.DOWN,
|
||||
HexagonAngle.LEFT_DOWN,
|
||||
HexagonAngle.LEFT_UP,
|
||||
]
|
||||
points = gen_points(hexagon_angles, LED_PER_STRIP, startPos)
|
||||
|
||||
print(points)
|
||||
|
||||
|
||||
|
||||
def simple_test() -> None:
|
||||
startPos = Point(0, 0)
|
||||
hexagon_angles = [
|
||||
HexagonAngle.UP,
|
||||
]
|
||||
points = gen_points(hexagon_angles, LED_PER_STRIP, startPos)
|
||||
print(points)
|
||||
# assert len(points) == LED_PER_STRIP + 1
|
||||
|
||||
def two_angle_test() -> None:
|
||||
startPos = Point(0, 0)
|
||||
hexagon_angles = [
|
||||
HexagonAngle.UP,
|
||||
HexagonAngle.UP,
|
||||
]
|
||||
points = gen_points(hexagon_angles, LED_PER_STRIP, startPos)
|
||||
print(points)
|
||||
# assert len(points) == LED_PER_STRIP * 2, f"Expected {LED_PER_STRIP * 2} points, got {len(points)} points"
|
||||
|
||||
|
||||
|
||||
def two_angle_test2() -> None:
|
||||
print("two_angle_test2")
|
||||
startPos = Point(0, 0)
|
||||
hexagon_angles = [
|
||||
HexagonAngle.UP,
|
||||
HexagonAngle.DOWN,
|
||||
]
|
||||
points = gen_points(hexagon_angles, LED_PER_STRIP, startPos)
|
||||
print(points)
|
||||
# assert len(points) == LED_PER_STRIP * 2, f"Expected {LED_PER_STRIP * 2} points, got {len(points)} points"
|
||||
|
||||
# Red is defined by this instruction tutorial: https://voidstar.dozuki.com/Guide/Chromance+Assembly+Instructions/6
|
||||
def find_red_anchor_point() -> list[Point]:
|
||||
hexagon_angles = [
|
||||
HexagonAngle.LEFT_UP,
|
||||
HexagonAngle.LEFT_UP,
|
||||
HexagonAngle.UP,
|
||||
HexagonAngle.RIGHT_UP,
|
||||
]
|
||||
points = gen_points(hexagon_angles, LED_PER_STRIP, Point(0, 0), add_last=True)
|
||||
return points
|
||||
|
||||
def find_green_anchore_point() -> list[Point]:
|
||||
hexagon_angles = [
|
||||
HexagonAngle.RIGHT_UP,
|
||||
HexagonAngle.RIGHT_UP,
|
||||
HexagonAngle.UP,
|
||||
]
|
||||
points = gen_points(hexagon_angles, LED_PER_STRIP, Point(0, 0), add_last=True)
|
||||
return points
|
||||
|
||||
|
||||
RED_ANCHOR_POINT = find_red_anchor_point()[-1]
|
||||
BLACK_ANCHOR_POINT = Point(0,0) # Black
|
||||
GREEN_ANCHOR_POINT = find_green_anchore_point()[-1]
|
||||
BLUE_ANCHOR_POINT = Point(0, 0)
|
||||
|
||||
|
||||
def generate_red_points() -> list[Point]:
|
||||
starting_point = RED_ANCHOR_POINT.copy()
|
||||
hexagon_angles = [
|
||||
HexagonAngle.UP,
|
||||
HexagonAngle.LEFT_UP,
|
||||
HexagonAngle.LEFT_DOWN,
|
||||
HexagonAngle.DOWN,
|
||||
HexagonAngle.RIGHT_DOWN,
|
||||
HexagonAngle.UP,
|
||||
HexagonAngle.LEFT_UP
|
||||
]
|
||||
points = gen_points(hexagon_angles, LED_PER_STRIP, starting_point, exclude=[5])
|
||||
return points
|
||||
|
||||
|
||||
def generate_black_points() -> list[Point]:
|
||||
starting_point = BLACK_ANCHOR_POINT.copy()
|
||||
hexagon_angles = [
|
||||
HexagonAngle.LEFT_UP,
|
||||
HexagonAngle.LEFT_UP,
|
||||
HexagonAngle.UP,
|
||||
HexagonAngle.RIGHT_UP,
|
||||
HexagonAngle.RIGHT_DOWN,
|
||||
HexagonAngle.DOWN,
|
||||
HexagonAngle.LEFT_DOWN,
|
||||
HexagonAngle.UP,
|
||||
HexagonAngle.LEFT_UP,
|
||||
HexagonAngle.UP,
|
||||
HexagonAngle.RIGHT_UP,
|
||||
]
|
||||
points = gen_points(hexagon_angles, LED_PER_STRIP, starting_point)
|
||||
return points
|
||||
|
||||
|
||||
def generate_green_points() -> list[Point]:
|
||||
starting_point = GREEN_ANCHOR_POINT.copy()
|
||||
hexagon_angles = [
|
||||
HexagonAngle.RIGHT_UP,
|
||||
HexagonAngle.UP,
|
||||
HexagonAngle.LEFT_UP,
|
||||
HexagonAngle.LEFT_DOWN,
|
||||
HexagonAngle.DOWN,
|
||||
HexagonAngle.RIGHT_DOWN, # skip
|
||||
HexagonAngle.LEFT_DOWN, # skip
|
||||
HexagonAngle.LEFT_UP,
|
||||
HexagonAngle.UP,
|
||||
HexagonAngle.RIGHT_UP,
|
||||
HexagonAngle.LEFT_UP,
|
||||
HexagonAngle.LEFT_DOWN,
|
||||
HexagonAngle.RIGHT_DOWN,
|
||||
HexagonAngle.RIGHT_UP, # skip
|
||||
HexagonAngle.RIGHT_DOWN,
|
||||
]
|
||||
points = gen_points(hexagon_angles, LED_PER_STRIP, starting_point, exclude=[5,6,13])
|
||||
return points
|
||||
|
||||
def generate_blue_points() -> list[Point]:
|
||||
starting_point = BLUE_ANCHOR_POINT.copy()
|
||||
hexagon_angles = [
|
||||
HexagonAngle.RIGHT_UP,
|
||||
HexagonAngle.RIGHT_UP,
|
||||
HexagonAngle.UP,
|
||||
HexagonAngle.LEFT_UP,
|
||||
HexagonAngle.LEFT_DOWN,
|
||||
HexagonAngle.LEFT_DOWN,
|
||||
HexagonAngle.RIGHT_DOWN, # skip
|
||||
HexagonAngle.RIGHT_DOWN,
|
||||
HexagonAngle.UP,
|
||||
HexagonAngle.RIGHT_UP,
|
||||
HexagonAngle.UP,
|
||||
HexagonAngle.RIGHT_UP,
|
||||
]
|
||||
points = gen_points(hexagon_angles, LED_PER_STRIP, starting_point, exclude=[6])
|
||||
return points
|
||||
|
||||
def unit_test() -> None:
|
||||
#simple_test()
|
||||
#two_angle_test()
|
||||
out = {}
|
||||
map = out.setdefault("map", {})
|
||||
map.update({
|
||||
"red_segment": Point.toJson(generate_red_points()),
|
||||
"back_segment": Point.toJson(generate_black_points()),
|
||||
"green_segment": Point.toJson(generate_green_points()),
|
||||
"blue_segment": Point.toJson(generate_blue_points()),
|
||||
})
|
||||
print(json.dumps(out))
|
||||
# write it out to a file
|
||||
with open("output.json", "w") as f:
|
||||
f.write(json.dumps(out))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unit_test()
|
||||
158
.pio/libdeps/esp01_1m/FastLED/examples/Chromancer/mapping.h
Normal file
158
.pio/libdeps/esp01_1m/FastLED/examples/Chromancer/mapping.h
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Maps hex topology onto LED's
|
||||
* (C) Voidstar Lab LLC 2021
|
||||
*/
|
||||
|
||||
#ifndef MAPPING_H_
|
||||
#define MAPPING_H_
|
||||
|
||||
// I accidentally noted these down 1-indexed and I'm too tired to adjust them
|
||||
#define headof(S) ((S - 1) * 14)
|
||||
#define tailof(S) (headof(S) + 13)
|
||||
|
||||
// Beam 0 is at 12:00 and advance clockwise
|
||||
// -1 means nothing connected on that side
|
||||
int nodeConnections[25][6] = {
|
||||
{-1, -1, 1, -1, 0, -1},
|
||||
{-1, -1, 3, -1, 2, -1},
|
||||
{-1, -1, 5, -1, 4, -1},
|
||||
{-1, 0, 6, 12, -1, -1},
|
||||
{-1, 2, 8, 14, 7, 1},
|
||||
|
||||
{-1, 4, 10, 16, 9, 3},
|
||||
{-1, -1, -1, 18, 11, 5},
|
||||
{-1, 7, -1, 13, -1, 6},
|
||||
{-1, 9, -1, 15, -1, 8},
|
||||
{-1, 11, -1, 17, -1, 10},
|
||||
|
||||
{12, -1, 19, -1, -1, -1},
|
||||
{14, -1, 21, -1, 20, -1},
|
||||
{16, -1, 23, -1, 22, -1},
|
||||
{18, -1, -1, -1, 24, -1},
|
||||
{13, 20, 25, 29, -1, -1},
|
||||
|
||||
{15, 22, 27, 31, 26, 21},
|
||||
{17, 24, -1, 33, 28, 23},
|
||||
{-1, 26, -1, 30, -1, 25},
|
||||
{-1, 28, -1, 32, -1, 27},
|
||||
{29, -1, 34, -1, -1, -1},
|
||||
|
||||
{31, -1, 36, -1, 35, -1},
|
||||
{33, -1, -1, -1, 37, -1},
|
||||
{30, 35, 38, -1, -1, 34},
|
||||
{32, 37, -1, -1, 39, 36},
|
||||
{-1, 39, -1, -1, -1, 38}
|
||||
};
|
||||
|
||||
// First member: Node closer to ceiling
|
||||
// Second: Node closer to floor
|
||||
int segmentConnections[40][2] = {
|
||||
{0, 3},
|
||||
{0, 4},
|
||||
{1, 4},
|
||||
{1, 5},
|
||||
{2, 5},
|
||||
{2, 6},
|
||||
{3, 7},
|
||||
{4, 7},
|
||||
{4, 8},
|
||||
{5, 8},
|
||||
{5, 9},
|
||||
{6, 9}, // ayy
|
||||
{3, 10},
|
||||
{7, 14},
|
||||
{4, 11},
|
||||
{8, 15},
|
||||
{5, 12},
|
||||
{9, 16},
|
||||
{6, 13},
|
||||
{10, 14},
|
||||
{11, 14},
|
||||
{11, 15},
|
||||
{12, 15},
|
||||
{12, 16},
|
||||
{13, 16},
|
||||
{14, 17},
|
||||
{15, 17},
|
||||
{15, 18},
|
||||
{16, 18},
|
||||
{14, 19},
|
||||
{17, 22},
|
||||
{15, 20},
|
||||
{18, 23},
|
||||
{16, 21},
|
||||
{19, 22},
|
||||
{20, 22},
|
||||
{20, 23},
|
||||
{21, 23},
|
||||
{22, 24},
|
||||
{23, 24}
|
||||
};
|
||||
|
||||
// First member: Strip number
|
||||
// Second: LED index closer to ceiling
|
||||
// Third: LED index closer to floor
|
||||
int ledAssignments[40][3] = {
|
||||
{2, headof(3), tailof(3)},
|
||||
{2, tailof(2), headof(2)},
|
||||
{1, headof(10), tailof(10)},
|
||||
{1, tailof(9), headof(9)},
|
||||
{1, headof(4), tailof(4)},
|
||||
{1, tailof(3), headof(3)},
|
||||
|
||||
{2, tailof(6), headof(6)},
|
||||
{3, tailof(11), headof(11)},
|
||||
{1, headof(11), tailof(11)},
|
||||
{1, tailof(8), headof(8)},
|
||||
{1, headof(12), tailof(12)},
|
||||
{0, tailof(11), headof(11)},
|
||||
|
||||
{2, headof(4), tailof(4)},
|
||||
{3, tailof(10), headof(10)},
|
||||
{2, tailof(1), headof(1)},
|
||||
{1, tailof(7), headof(7)},
|
||||
{1, headof(5), tailof(5)},
|
||||
{0, tailof(10), headof(10)},
|
||||
{1, tailof(2), headof(2)},
|
||||
|
||||
{2, headof(5), tailof(5)},
|
||||
{3, tailof(4), headof(4)},
|
||||
{3, headof(5), tailof(5)},
|
||||
{0, headof(5), tailof(5)},
|
||||
{0, tailof(4), headof(4)},
|
||||
{1, tailof(1), headof(1)},
|
||||
|
||||
{3, tailof(9), headof(9)},
|
||||
{0, headof(6), tailof(6)},
|
||||
{1, tailof(6), headof(6)},
|
||||
{0, tailof(9), headof(9)},
|
||||
|
||||
{3, tailof(3), headof(3)},
|
||||
{3, tailof(8), headof(8)},
|
||||
{3, headof(6), tailof(6)},
|
||||
{0, tailof(8), headof(8)},
|
||||
{0, tailof(3), headof(3)},
|
||||
|
||||
{3, tailof(2), headof(2)},
|
||||
{3, headof(7), tailof(7)},
|
||||
{0, headof(7), tailof(7)},
|
||||
{0, tailof(2), headof(2)},
|
||||
|
||||
{3, tailof(1), headof(1)},
|
||||
{0, tailof(1), headof(1)}
|
||||
};
|
||||
|
||||
// Border nodes are on the very edge of the network.
|
||||
// Ripples fired here don't look very impressive.
|
||||
int numberOfBorderNodes = 10;
|
||||
int borderNodes[] = {0, 1, 2, 3, 6, 10, 13, 19, 21, 24};
|
||||
|
||||
// Cube nodes link three equiangular segments
|
||||
// Firing ripples that always turn in one direction will draw a cube
|
||||
int numberOfCubeNodes = 8;
|
||||
int cubeNodes[] = {7, 8, 9, 11, 12, 17, 18};
|
||||
|
||||
// Firing ripples that always turn in one direction will draw a starburst
|
||||
int starburstNode = 15;
|
||||
|
||||
#endif
|
||||
File diff suppressed because one or more lines are too long
426
.pio/libdeps/esp01_1m/FastLED/examples/Chromancer/ripple.h
Normal file
426
.pio/libdeps/esp01_1m/FastLED/examples/Chromancer/ripple.h
Normal file
@@ -0,0 +1,426 @@
|
||||
/*
|
||||
A dot animation that travels along rails
|
||||
(C) Voidstar Lab LLC 2021
|
||||
*/
|
||||
|
||||
#ifndef RIPPLE_H_
|
||||
#define RIPPLE_H_
|
||||
|
||||
// WARNING: These slow things down enough to affect performance. Don't turn on unless you need them!
|
||||
//#define DEBUG_ADVANCEMENT // Print debug messages about ripples' movement
|
||||
//#define DEBUG_RENDERING // Print debug messages about translating logical to actual position
|
||||
|
||||
#include "FastLED.h"
|
||||
#include "mapping.h"
|
||||
|
||||
enum rippleState {
|
||||
dead,
|
||||
withinNode, // Ripple isn't drawn as it passes through a node to keep the speed consistent
|
||||
travelingUpwards,
|
||||
travelingDownwards
|
||||
};
|
||||
|
||||
enum rippleBehavior {
|
||||
weaksauce = 0,
|
||||
feisty = 1,
|
||||
angry = 2,
|
||||
alwaysTurnsRight = 3,
|
||||
alwaysTurnsLeft = 4
|
||||
};
|
||||
|
||||
float fmap(float x, float in_min, float in_max, float out_min, float out_max) {
|
||||
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
|
||||
}
|
||||
|
||||
class Ripple {
|
||||
public:
|
||||
Ripple(int id) : rippleId(id) {
|
||||
Serial.print("Instanced ripple #");
|
||||
Serial.println(rippleId);
|
||||
}
|
||||
|
||||
rippleState state = dead;
|
||||
unsigned long color;
|
||||
|
||||
/*
|
||||
If within a node: 0 is node, 1 is direction
|
||||
If traveling, 0 is segment, 1 is LED position from bottom
|
||||
*/
|
||||
int position[2];
|
||||
|
||||
// Place the Ripple in a node
|
||||
void start(byte n, byte d, unsigned long c, float s, unsigned long l, byte b) {
|
||||
color = c;
|
||||
speed = s;
|
||||
lifespan = l;
|
||||
behavior = b;
|
||||
|
||||
birthday = millis();
|
||||
pressure = 0;
|
||||
state = withinNode;
|
||||
|
||||
position[0] = n;
|
||||
position[1] = d;
|
||||
|
||||
justStarted = true;
|
||||
|
||||
Serial.print("Ripple ");
|
||||
Serial.print(rippleId);
|
||||
Serial.print(" starting at node ");
|
||||
Serial.print(position[0]);
|
||||
Serial.print(" direction ");
|
||||
Serial.println(position[1]);
|
||||
}
|
||||
|
||||
void advance(byte ledColors[40][14][3]) {
|
||||
unsigned long age = millis() - birthday;
|
||||
|
||||
if (state == dead)
|
||||
return;
|
||||
|
||||
pressure += fmap(float(age), 0.0, float(lifespan), speed, 0.0); // Ripple slows down as it ages
|
||||
// TODO: Motion of ripple is severely affected by loop speed. Make it time invariant
|
||||
|
||||
if (pressure < 1 && (state == travelingUpwards || state == travelingDownwards)) {
|
||||
// Ripple is visible but hasn't moved - render it to avoid flickering
|
||||
renderLed(ledColors, age);
|
||||
}
|
||||
|
||||
while (pressure >= 1) {
|
||||
#ifdef DEBUG_ADVANCEMENT
|
||||
Serial.print("Ripple ");
|
||||
Serial.print(rippleId);
|
||||
Serial.println(" advancing:");
|
||||
#endif
|
||||
|
||||
switch (state) {
|
||||
case withinNode: {
|
||||
if (justStarted) {
|
||||
justStarted = false;
|
||||
}
|
||||
else {
|
||||
#ifdef DEBUG_ADVANCEMENT
|
||||
Serial.print(" Picking direction out of node ");
|
||||
Serial.print(position[0]);
|
||||
Serial.print(" with agr. ");
|
||||
Serial.println(behavior);
|
||||
#endif
|
||||
|
||||
int newDirection = -1;
|
||||
|
||||
int sharpLeft = (position[1] + 1) % 6;
|
||||
int wideLeft = (position[1] + 2) % 6;
|
||||
int forward = (position[1] + 3) % 6;
|
||||
int wideRight = (position[1] + 4) % 6;
|
||||
int sharpRight = (position[1] + 5) % 6;
|
||||
|
||||
if (behavior <= 2) { // Semi-random aggressive turn mode
|
||||
// The more aggressive a ripple, the tighter turns it wants to make.
|
||||
// If there aren't any segments it can turn to, we need to adjust its behavior.
|
||||
byte anger = behavior;
|
||||
|
||||
while (newDirection < 0) {
|
||||
if (anger == 0) {
|
||||
int forwardConnection = nodeConnections[position[0]][forward];
|
||||
|
||||
if (forwardConnection < 0) {
|
||||
// We can't go straight ahead - we need to take a more aggressive angle
|
||||
#ifdef DEBUG_ADVANCEMENT
|
||||
Serial.println(" Can't go straight - picking more agr. path");
|
||||
#endif
|
||||
anger++;
|
||||
}
|
||||
else {
|
||||
#ifdef DEBUG_ADVANCEMENT
|
||||
Serial.println(" Going forward");
|
||||
#endif
|
||||
newDirection = forward;
|
||||
}
|
||||
}
|
||||
|
||||
if (anger == 1) {
|
||||
int leftConnection = nodeConnections[position[0]][wideLeft];
|
||||
int rightConnection = nodeConnections[position[0]][wideRight];
|
||||
|
||||
if (leftConnection >= 0 && rightConnection >= 0) {
|
||||
#ifdef DEBUG_ADVANCEMENT
|
||||
Serial.println(" Turning left or right at random");
|
||||
#endif
|
||||
newDirection = random(2) ? wideLeft : wideRight;
|
||||
}
|
||||
else if (leftConnection >= 0) {
|
||||
#ifdef DEBUG_ADVANCEMENT
|
||||
Serial.println(" Can only turn left");
|
||||
#endif
|
||||
newDirection = wideLeft;
|
||||
}
|
||||
else if (rightConnection >= 0) {
|
||||
#ifdef DEBUG_ADVANCEMENT
|
||||
Serial.println(" Can only turn right");
|
||||
#endif
|
||||
newDirection = wideRight;
|
||||
}
|
||||
else {
|
||||
#ifdef DEBUG_ADVANCEMENT
|
||||
Serial.println(" Can't make wide turn - picking more agr. path");
|
||||
#endif
|
||||
anger++; // Can't take shallow turn - must become more aggressive
|
||||
}
|
||||
}
|
||||
|
||||
if (anger == 2) {
|
||||
int leftConnection = nodeConnections[position[0]][sharpLeft];
|
||||
int rightConnection = nodeConnections[position[0]][sharpRight];
|
||||
|
||||
if (leftConnection >= 0 && rightConnection >= 0) {
|
||||
#ifdef DEBUG_ADVANCEMENT
|
||||
Serial.println(" Turning left or right at random");
|
||||
#endif
|
||||
newDirection = random(2) ? sharpLeft : sharpRight;
|
||||
}
|
||||
else if (leftConnection >= 0) {
|
||||
#ifdef DEBUG_ADVANCEMENT
|
||||
Serial.println(" Can only turn left");
|
||||
#endif
|
||||
newDirection = sharpLeft;
|
||||
}
|
||||
else if (rightConnection >= 0) {
|
||||
#ifdef DEBUG_ADVANCEMENT
|
||||
Serial.println(" Can only turn right");
|
||||
#endif
|
||||
newDirection = sharpRight;
|
||||
}
|
||||
else {
|
||||
#ifdef DEBUG_ADVANCEMENT
|
||||
Serial.println(" Can't make tight turn - picking less agr. path");
|
||||
#endif
|
||||
anger--; // Can't take tight turn - must become less aggressive
|
||||
}
|
||||
}
|
||||
|
||||
// Note that this can't handle some circumstances,
|
||||
// like a node with segments in nothing but the 0 and 3 positions.
|
||||
// Good thing we don't have any of those!
|
||||
}
|
||||
}
|
||||
else if (behavior == alwaysTurnsRight) {
|
||||
for (int i = 1; i < 6; i++) {
|
||||
int possibleDirection = (position[1] + i) % 6;
|
||||
|
||||
if (nodeConnections[position[0]][possibleDirection] >= 0) {
|
||||
newDirection = possibleDirection;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ADVANCEMENT
|
||||
Serial.println(" Turning as rightward as possible");
|
||||
#endif
|
||||
}
|
||||
else if (behavior == alwaysTurnsLeft) {
|
||||
for (int i = 5; i >= 1; i--) {
|
||||
int possibleDirection = (position[1] + i) % 6;
|
||||
|
||||
if (nodeConnections[position[0]][possibleDirection] >= 0) {
|
||||
newDirection = possibleDirection;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ADVANCEMENT
|
||||
Serial.println(" Turning as leftward as possible");
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ADVANCEMENT
|
||||
Serial.print(" Leaving node ");
|
||||
Serial.print(position[0]);
|
||||
Serial.print(" in direction ");
|
||||
Serial.println(newDirection);
|
||||
#endif
|
||||
|
||||
position[1] = newDirection;
|
||||
}
|
||||
|
||||
position[0] = nodeConnections[position[0]][position[1]]; // Look up which segment we're on
|
||||
|
||||
#ifdef DEBUG_ADVANCEMENT
|
||||
Serial.print(" and entering segment ");
|
||||
Serial.println(position[0]);
|
||||
#endif
|
||||
|
||||
if (position[1] == 5 || position[1] == 0 || position[1] == 1) { // Top half of the node
|
||||
#ifdef DEBUG_ADVANCEMENT
|
||||
Serial.println(" (starting at bottom)");
|
||||
#endif
|
||||
state = travelingUpwards;
|
||||
position[1] = 0; // Starting at bottom of segment
|
||||
}
|
||||
else {
|
||||
#ifdef DEBUG_ADVANCEMENT
|
||||
Serial.println(" (starting at top)");
|
||||
#endif
|
||||
state = travelingDownwards;
|
||||
position[1] = 13; // Starting at top of 14-LED-long strip
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case travelingUpwards: {
|
||||
position[1]++;
|
||||
|
||||
if (position[1] >= 14) {
|
||||
// We've reached the top!
|
||||
#ifdef DEBUG_ADVANCEMENT
|
||||
Serial.print(" Reached top of seg. ");
|
||||
Serial.println(position[0]);
|
||||
#endif
|
||||
// Enter the new node.
|
||||
int segment = position[0];
|
||||
position[0] = segmentConnections[position[0]][0];
|
||||
for (int i = 0; i < 6; i++) {
|
||||
// Figure out from which direction the ripple is entering the node.
|
||||
// Allows us to exit in an appropriately aggressive direction.
|
||||
int incomingConnection = nodeConnections[position[0]][i];
|
||||
if (incomingConnection == segment)
|
||||
position[1] = i;
|
||||
}
|
||||
#ifdef DEBUG_ADVANCEMENT
|
||||
Serial.print(" Entering node ");
|
||||
Serial.print(position[0]);
|
||||
Serial.print(" from direction ");
|
||||
Serial.println(position[1]);
|
||||
#endif
|
||||
state = withinNode;
|
||||
}
|
||||
else {
|
||||
#ifdef DEBUG_ADVANCEMENT
|
||||
Serial.print(" Moved up to seg. ");
|
||||
Serial.print(position[0]);
|
||||
Serial.print(" LED ");
|
||||
Serial.println(position[1]);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case travelingDownwards: {
|
||||
position[1]--;
|
||||
if (position[1] < 0) {
|
||||
// We've reached the bottom!
|
||||
#ifdef DEBUG_ADVANCEMENT
|
||||
Serial.print(" Reached bottom of seg. ");
|
||||
Serial.println(position[0]);
|
||||
#endif
|
||||
// Enter the new node.
|
||||
int segment = position[0];
|
||||
position[0] = segmentConnections[position[0]][1];
|
||||
for (int i = 0; i < 6; i++) {
|
||||
// Figure out from which direction the ripple is entering the node.
|
||||
// Allows us to exit in an appropriately aggressive direction.
|
||||
int incomingConnection = nodeConnections[position[0]][i];
|
||||
if (incomingConnection == segment)
|
||||
position[1] = i;
|
||||
}
|
||||
#ifdef DEBUG_ADVANCEMENT
|
||||
Serial.print(" Entering node ");
|
||||
Serial.print(position[0]);
|
||||
Serial.print(" from direction ");
|
||||
Serial.println(position[1]);
|
||||
#endif
|
||||
state = withinNode;
|
||||
}
|
||||
else {
|
||||
#ifdef DEBUG_ADVANCEMENT
|
||||
Serial.print(" Moved down to seg. ");
|
||||
Serial.print(position[0]);
|
||||
Serial.print(" LED ");
|
||||
Serial.println(position[1]);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
pressure -= 1;
|
||||
|
||||
if (state == travelingUpwards || state == travelingDownwards) {
|
||||
// Ripple is visible - render it
|
||||
renderLed(ledColors, age);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ADVANCEMENT
|
||||
Serial.print(" Age is now ");
|
||||
Serial.print(age);
|
||||
Serial.print('/');
|
||||
Serial.println(lifespan);
|
||||
#endif
|
||||
|
||||
if (lifespan && age >= lifespan) {
|
||||
// We dead
|
||||
#ifdef DEBUG_ADVANCEMENT
|
||||
Serial.println(" Lifespan is up! Ripple is dead.");
|
||||
#endif
|
||||
state = dead;
|
||||
position[0] = position[1] = pressure = age = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
float speed; // Each loop, ripples move this many LED's.
|
||||
unsigned long lifespan; // The ripple stops after this many milliseconds
|
||||
|
||||
/*
|
||||
0: Always goes straight ahead if possible
|
||||
1: Can take 60-degree turns
|
||||
2: Can take 120-degree turns
|
||||
*/
|
||||
byte behavior;
|
||||
|
||||
bool justStarted = false;
|
||||
|
||||
float pressure; // When Pressure reaches 1, ripple will move
|
||||
unsigned long birthday; // Used to track age of ripple
|
||||
|
||||
static byte rippleCount; // Used to give them unique ID's
|
||||
byte rippleId; // Used to identify this ripple in debug output
|
||||
|
||||
void renderLed(byte ledColors[40][14][3], unsigned long age) {
|
||||
int strip = ledAssignments[position[0]][0];
|
||||
int led = ledAssignments[position[0]][2] + position[1];
|
||||
FL_UNUSED(strip);
|
||||
FL_UNUSED(led);
|
||||
|
||||
int red = ledColors[position[0]][position[1]][0];
|
||||
int green = ledColors[position[0]][position[1]][1];
|
||||
int blue = ledColors[position[0]][position[1]][2];
|
||||
|
||||
ledColors[position[0]][position[1]][0] = byte(min(255, max(0, int(fmap(float(age), 0.0, float(lifespan), (color >> 8) & 0xFF, 0.0)) + red)));
|
||||
ledColors[position[0]][position[1]][1] = byte(min(255, max(0, int(fmap(float(age), 0.0, float(lifespan), (color >> 16) & 0xFF, 0.0)) + green)));
|
||||
ledColors[position[0]][position[1]][2] = byte(min(255, max(0, int(fmap(float(age), 0.0, float(lifespan), color & 0xFF, 0.0)) + blue)));
|
||||
|
||||
#ifdef DEBUG_RENDERING
|
||||
Serial.print("Rendering ripple position (");
|
||||
Serial.print(position[0]);
|
||||
Serial.print(',');
|
||||
Serial.print(position[1]);
|
||||
Serial.print(") at Strip ");
|
||||
Serial.print(strip);
|
||||
Serial.print(", LED ");
|
||||
Serial.print(led);
|
||||
Serial.print(", color 0x");
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (ledColors[position[0]][position[1]][i] <= 0x0F)
|
||||
Serial.print('0');
|
||||
Serial.print(ledColors[position[0]][position[1]][i], HEX);
|
||||
}
|
||||
Serial.println();
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because one or more lines are too long
148
.pio/libdeps/esp01_1m/FastLED/examples/ColorBoost/ColorBoost.h
Normal file
148
.pio/libdeps/esp01_1m/FastLED/examples/ColorBoost/ColorBoost.h
Normal file
@@ -0,0 +1,148 @@
|
||||
/// @file ColorBoost.h
|
||||
/// @brief Demo of CRGB::colorBoost() for video display on WS2812 LEDs using animated rainbow effect
|
||||
/// (based on Pride2015 by Mark Kriegsman)
|
||||
/// @example ColorBoost.ino
|
||||
///
|
||||
/// 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`
|
||||
/// 4. When the compiler is done a web page will open.
|
||||
|
||||
// This demo shows use of CRGB::colorBoost() to boost saturation for better LED display, compared to
|
||||
// normal colors and colors adjusted with gamma correction.
|
||||
// The demo involves animated, ever-changing rainbows (based on Pride2015 by Mark Kriegsman).
|
||||
|
||||
#include "FastLED.h"
|
||||
#include "fl/ease.h"
|
||||
|
||||
using namespace fl;
|
||||
|
||||
UITitle title("ColorBoost");
|
||||
UIDescription description("CRGB::colorBoost() is a function that boosts the saturation of a color without decimating the color from 8 bit -> gamma -> 8 bit (leaving only 8 colors for each component). Use the dropdown menus to select different easing functions for saturation and luminance. Use legacy gfx mode (?gfx=0) for best results.");
|
||||
|
||||
UISlider satSlider("Saturation", 60, 0, 255, 1);
|
||||
|
||||
// Create dropdown with descriptive ease function names
|
||||
fl::string easeOptions[] = {
|
||||
"None",
|
||||
"In Quad",
|
||||
"Out Quad",
|
||||
"In-Out Quad",
|
||||
"In Cubic",
|
||||
"Out Cubic",
|
||||
"In-Out Cubic",
|
||||
"In Sine",
|
||||
"Out Sine",
|
||||
"In-Out Sine"
|
||||
};
|
||||
UIDropdown saturationFunction("Saturation Function", easeOptions);
|
||||
UIDropdown luminanceFunction("Luminance Function", easeOptions);
|
||||
|
||||
// Group related color boost UI elements using UIGroup template multi-argument constructor
|
||||
UIGroup colorBoostControls("Color Boost", satSlider, saturationFunction, luminanceFunction);
|
||||
|
||||
#define DATA_PIN 2
|
||||
#define LED_TYPE WS2812
|
||||
#define COLOR_ORDER GRB
|
||||
#define WIDTH 22
|
||||
#define HEIGHT 22
|
||||
#define NUM_LEDS (WIDTH * HEIGHT)
|
||||
#define BRIGHTNESS 150
|
||||
|
||||
CRGB leds[NUM_LEDS];
|
||||
|
||||
// fl::ScreenMap screenmap(lut.data(), lut.size());
|
||||
// fl::ScreenMap screenmap(NUM_LEDS);
|
||||
fl::XYMap xyMap =
|
||||
fl::XYMap::constructRectangularGrid(WIDTH, HEIGHT);
|
||||
|
||||
void setup() {
|
||||
|
||||
// tell FastLED about the LED strip configuration
|
||||
FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS)
|
||||
.setCorrection(TypicalLEDStrip)
|
||||
.setScreenMap(xyMap);
|
||||
// set master brightness control
|
||||
FastLED.setBrightness(BRIGHTNESS);
|
||||
|
||||
// Set default dropdown selections
|
||||
saturationFunction.setSelectedIndex(1); // "In Quad"
|
||||
luminanceFunction.setSelectedIndex(0); // "None"
|
||||
}
|
||||
|
||||
EaseType getEaseType(int value) {
|
||||
switch (value) {
|
||||
case 0: return EASE_NONE;
|
||||
case 1: return EASE_IN_QUAD;
|
||||
case 2: return EASE_OUT_QUAD;
|
||||
case 3: return EASE_IN_OUT_QUAD;
|
||||
case 4: return EASE_IN_CUBIC;
|
||||
case 5: return EASE_OUT_CUBIC;
|
||||
case 6: return EASE_IN_OUT_CUBIC;
|
||||
case 7: return EASE_IN_SINE;
|
||||
case 8: return EASE_OUT_SINE;
|
||||
case 9: return EASE_IN_OUT_SINE;
|
||||
}
|
||||
FL_ASSERT(false, "Invalid ease type");
|
||||
return EASE_NONE;
|
||||
}
|
||||
|
||||
// Animated rainbow wave effect (Pride2015), with matrix divided into three segments to compare:
|
||||
// - Normal colors (top)
|
||||
// - Colors optimized using colorBoost() (middle)
|
||||
// - Colors adjusted using gamma correction (bottom)
|
||||
void rainbowWave() {
|
||||
// Use millis() for consistent timing across different devices
|
||||
// Scale down millis() to get appropriate animation speed
|
||||
uint16_t time = millis() / 16; // Adjust divisor to control wave speed
|
||||
uint8_t hueOffset = millis() / 32; // Adjust divisor to control hue rotation speed
|
||||
|
||||
// Iterate through the entire matrix
|
||||
for (uint16_t y = 0; y < HEIGHT; y++) {
|
||||
for (uint16_t x = 0; x < WIDTH; x++) {
|
||||
// Create a wave pattern using sine function based on position and time
|
||||
uint8_t wave = sin8(time + (x * 8));
|
||||
|
||||
// Calculate hue based on position and time for rainbow effect
|
||||
uint8_t hue = hueOffset + (x * 255 / WIDTH);
|
||||
|
||||
// Use wave for both saturation and brightness variation
|
||||
// uint8_t sat = 255 - (wave / 4); // Subtle saturation variation
|
||||
uint8_t bri = 128 + (wave / 2); // Brightness wave from 128 to 255
|
||||
|
||||
// Create the original color using HSV
|
||||
CRGB original_color = CHSV(hue, satSlider.value(), bri);
|
||||
|
||||
if (y > HEIGHT / 3 * 2) {
|
||||
// Upper third - original colors
|
||||
leds[xyMap(x, y)] = original_color;
|
||||
} else if (y > HEIGHT / 3) {
|
||||
// Middle third - colors transformed with colorBoost()
|
||||
EaseType sat_ease = getEaseType(saturationFunction.as_int());
|
||||
EaseType lum_ease = getEaseType(luminanceFunction.as_int());
|
||||
leds[xyMap(x, y)] = original_color.colorBoost(sat_ease, lum_ease);
|
||||
} else {
|
||||
// Lower third - colors transformed using gamma correction
|
||||
float r = original_color.r / 255.f;
|
||||
float g = original_color.g / 255.f;
|
||||
float b = original_color.b / 255.f;
|
||||
|
||||
r = pow(r, 2.0);
|
||||
g = pow(g, 2.0);
|
||||
b = pow(b, 2.0);
|
||||
|
||||
r = r * 255.f;
|
||||
g = g * 255.f;
|
||||
b = b * 255.f;
|
||||
|
||||
leds[xyMap(x, y)] = CRGB(r, g, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
rainbowWave();
|
||||
FastLED.show();
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#include "fl/sketch_macros.h"
|
||||
|
||||
#if SKETCH_HAS_LOTS_OF_MEMORY
|
||||
|
||||
#include "./ColorBoost.h"
|
||||
|
||||
#else
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.println("Not enough memory");
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,203 @@
|
||||
/// @file ColorPalette.ino
|
||||
/// @brief Demonstrates how to use @ref ColorPalettes
|
||||
/// @example ColorPalette.ino
|
||||
|
||||
#include <FastLED.h>
|
||||
|
||||
#define LED_PIN 5
|
||||
#define NUM_LEDS 50
|
||||
#define BRIGHTNESS 64
|
||||
#define LED_TYPE WS2811
|
||||
#define COLOR_ORDER GRB
|
||||
CRGB leds[NUM_LEDS];
|
||||
|
||||
#define UPDATES_PER_SECOND 100
|
||||
|
||||
// This example shows several ways to set up and use 'palettes' of colors
|
||||
// with FastLED.
|
||||
//
|
||||
// These compact palettes provide an easy way to re-colorize your
|
||||
// animation on the fly, quickly, easily, and with low overhead.
|
||||
//
|
||||
// USING palettes is MUCH simpler in practice than in theory, so first just
|
||||
// run this sketch, and watch the pretty lights as you then read through
|
||||
// the code. Although this sketch has eight (or more) different color schemes,
|
||||
// the entire sketch compiles down to about 6.5K on AVR.
|
||||
//
|
||||
// FastLED provides a few pre-configured color palettes, and makes it
|
||||
// extremely easy to make up your own color schemes with palettes.
|
||||
//
|
||||
// Some notes on the more abstract 'theory and practice' of
|
||||
// FastLED compact palettes are at the bottom of this file.
|
||||
|
||||
|
||||
|
||||
CRGBPalette16 currentPalette;
|
||||
TBlendType currentBlending;
|
||||
|
||||
extern CRGBPalette16 myRedWhiteBluePalette;
|
||||
extern const TProgmemPalette16 myRedWhiteBluePalette_p FL_PROGMEM;
|
||||
|
||||
// If you are using the fastled compiler, then you must declare your functions
|
||||
// before you use them. This is standard in C++ and C projects, but ino's are
|
||||
// special in that they do this for you. Eventually we will try to emulate this
|
||||
// feature ourselves but in the meantime you'll have to declare your functions
|
||||
// before you use them if you want to use our compiler.
|
||||
void ChangePalettePeriodically();
|
||||
void FillLEDsFromPaletteColors(uint8_t colorIndex);
|
||||
void SetupPurpleAndGreenPalette();
|
||||
void SetupTotallyRandomPalette();
|
||||
void SetupBlackAndWhiteStripedPalette();
|
||||
|
||||
|
||||
void setup() {
|
||||
delay( 3000 ); // power-up safety delay
|
||||
FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
|
||||
FastLED.setBrightness( BRIGHTNESS );
|
||||
|
||||
currentPalette = RainbowColors_p;
|
||||
currentBlending = LINEARBLEND;
|
||||
}
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
ChangePalettePeriodically();
|
||||
|
||||
static uint8_t startIndex = 0;
|
||||
startIndex = startIndex + 1; /* motion speed */
|
||||
|
||||
FillLEDsFromPaletteColors( startIndex);
|
||||
|
||||
FastLED.show();
|
||||
FastLED.delay(1000 / UPDATES_PER_SECOND);
|
||||
}
|
||||
|
||||
void FillLEDsFromPaletteColors( uint8_t colorIndex)
|
||||
{
|
||||
uint8_t brightness = 255;
|
||||
|
||||
for( int i = 0; i < NUM_LEDS; ++i) {
|
||||
leds[i] = ColorFromPalette( currentPalette, colorIndex, brightness, currentBlending);
|
||||
colorIndex += 3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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. All are shown here.
|
||||
|
||||
void ChangePalettePeriodically()
|
||||
{
|
||||
uint8_t secondHand = (millis() / 1000) % 60;
|
||||
static uint8_t lastSecond = 99;
|
||||
|
||||
if( lastSecond != secondHand) {
|
||||
lastSecond = secondHand;
|
||||
if( secondHand == 0) { currentPalette = RainbowColors_p; currentBlending = LINEARBLEND; }
|
||||
if( secondHand == 10) { currentPalette = RainbowStripeColors_p; currentBlending = NOBLEND; }
|
||||
if( secondHand == 15) { currentPalette = RainbowStripeColors_p; currentBlending = LINEARBLEND; }
|
||||
if( secondHand == 20) { SetupPurpleAndGreenPalette(); currentBlending = LINEARBLEND; }
|
||||
if( secondHand == 25) { SetupTotallyRandomPalette(); currentBlending = LINEARBLEND; }
|
||||
if( secondHand == 30) { SetupBlackAndWhiteStripedPalette(); currentBlending = NOBLEND; }
|
||||
if( secondHand == 35) { SetupBlackAndWhiteStripedPalette(); currentBlending = LINEARBLEND; }
|
||||
if( secondHand == 40) { currentPalette = CloudColors_p; currentBlending = LINEARBLEND; }
|
||||
if( secondHand == 45) { currentPalette = PartyColors_p; currentBlending = LINEARBLEND; }
|
||||
if( secondHand == 50) { currentPalette = myRedWhiteBluePalette_p; currentBlending = NOBLEND; }
|
||||
if( secondHand == 55) { currentPalette = myRedWhiteBluePalette_p; currentBlending = LINEARBLEND; }
|
||||
}
|
||||
}
|
||||
|
||||
// This function fills the palette with totally random colors.
|
||||
void SetupTotallyRandomPalette()
|
||||
{
|
||||
for( int i = 0; i < 16; ++i) {
|
||||
currentPalette[i] = CHSV( random8(), 255, random8());
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
currentPalette[0] = CRGB::White;
|
||||
currentPalette[4] = CRGB::White;
|
||||
currentPalette[8] = CRGB::White;
|
||||
currentPalette[12] = CRGB::White;
|
||||
|
||||
}
|
||||
|
||||
// This function sets up a palette of purple and green stripes.
|
||||
void SetupPurpleAndGreenPalette()
|
||||
{
|
||||
CRGB purple = CHSV( HUE_PURPLE, 255, 255);
|
||||
CRGB green = CHSV( HUE_GREEN, 255, 255);
|
||||
CRGB black = CRGB::Black;
|
||||
|
||||
currentPalette = CRGBPalette16(
|
||||
green, green, black, black,
|
||||
purple, purple, black, black,
|
||||
green, green, black, black,
|
||||
purple, purple, black, black );
|
||||
}
|
||||
|
||||
|
||||
// This example shows how to set up a static color palette
|
||||
// which is stored in PROGMEM (flash), which is almost always more
|
||||
// plentiful than RAM. A static PROGMEM palette like this
|
||||
// takes up 64 bytes of flash.
|
||||
const TProgmemPalette16 myRedWhiteBluePalette_p FL_PROGMEM =
|
||||
{
|
||||
CRGB::Red,
|
||||
CRGB::Gray, // 'white' is too bright compared to red and blue
|
||||
CRGB::Blue,
|
||||
CRGB::Black,
|
||||
|
||||
CRGB::Red,
|
||||
CRGB::Gray,
|
||||
CRGB::Blue,
|
||||
CRGB::Black,
|
||||
|
||||
CRGB::Red,
|
||||
CRGB::Red,
|
||||
CRGB::Gray,
|
||||
CRGB::Gray,
|
||||
CRGB::Blue,
|
||||
CRGB::Blue,
|
||||
CRGB::Black,
|
||||
CRGB::Black
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Additional notes on FastLED compact palettes:
|
||||
//
|
||||
// Normally, in computer graphics, the palette (or "color lookup table")
|
||||
// has 256 entries, each containing a specific 24-bit RGB color. You can then
|
||||
// index into the color palette using a simple 8-bit (one byte) value.
|
||||
// A 256-entry color palette takes up 768 bytes of RAM, which on Arduino
|
||||
// is quite possibly "too many" bytes.
|
||||
//
|
||||
// FastLED does offer traditional 256-element palettes, for setups that
|
||||
// can afford the 768-byte cost in RAM.
|
||||
//
|
||||
// However, FastLED also offers a compact alternative. FastLED offers
|
||||
// palettes that store 16 distinct entries, but can be accessed AS IF
|
||||
// they actually have 256 entries; this is accomplished by interpolating
|
||||
// between the 16 explicit entries to create fifteen intermediate palette
|
||||
// entries between each pair.
|
||||
//
|
||||
// So for example, if you set the first two explicit entries of a compact
|
||||
// palette to Green (0,255,0) and Blue (0,0,255), and then retrieved
|
||||
// the first sixteen entries from the virtual palette (of 256), you'd get
|
||||
// Green, followed by a smooth gradient from green-to-blue, and then Blue.
|
||||
@@ -0,0 +1,89 @@
|
||||
/// @file ColorTemperature.ino
|
||||
/// @brief Demonstrates how to use @ref ColorTemperature based color correction
|
||||
/// @example ColorTemperature.ino
|
||||
|
||||
#include <FastLED.h>
|
||||
|
||||
#define LED_PIN 3
|
||||
|
||||
// Information about the LED strip itself
|
||||
#define NUM_LEDS 60
|
||||
#define CHIPSET WS2811
|
||||
#define COLOR_ORDER GRB
|
||||
CRGB leds[NUM_LEDS];
|
||||
|
||||
#define BRIGHTNESS 128
|
||||
|
||||
|
||||
// FastLED provides two color-management controls:
|
||||
// (1) color correction settings for each LED strip, and
|
||||
// (2) master control of the overall output 'color temperature'
|
||||
//
|
||||
// THIS EXAMPLE demonstrates the second, "color temperature" control.
|
||||
// It shows a simple rainbow animation first with one temperature profile,
|
||||
// and a few seconds later, with a different temperature profile.
|
||||
//
|
||||
// The first pixel of the strip will show the color temperature.
|
||||
//
|
||||
// HELPFUL HINTS for "seeing" the effect in this demo:
|
||||
// * Don't look directly at the LED pixels. Shine the LEDs aganst
|
||||
// a white wall, table, or piece of paper, and look at the reflected light.
|
||||
//
|
||||
// * If you watch it for a bit, and then walk away, and then come back
|
||||
// to it, you'll probably be able to "see" whether it's currently using
|
||||
// the 'redder' or the 'bluer' temperature profile, even not counting
|
||||
// the lowest 'indicator' pixel.
|
||||
//
|
||||
//
|
||||
// FastLED provides these pre-conigured incandescent color profiles:
|
||||
// Candle, Tungsten40W, Tungsten100W, Halogen, CarbonArc,
|
||||
// HighNoonSun, DirectSunlight, OvercastSky, ClearBlueSky,
|
||||
// FastLED provides these pre-configured gaseous-light color profiles:
|
||||
// WarmFluorescent, StandardFluorescent, CoolWhiteFluorescent,
|
||||
// FullSpectrumFluorescent, GrowLightFluorescent, BlackLightFluorescent,
|
||||
// MercuryVapor, SodiumVapor, MetalHalide, HighPressureSodium,
|
||||
// FastLED also provides an "Uncorrected temperature" profile
|
||||
// UncorrectedTemperature;
|
||||
|
||||
#define TEMPERATURE_1 Tungsten100W
|
||||
#define TEMPERATURE_2 OvercastSky
|
||||
|
||||
// How many seconds to show each temperature before switching
|
||||
#define DISPLAYTIME 20
|
||||
// How many seconds to show black between switches
|
||||
#define BLACKTIME 3
|
||||
|
||||
void loop()
|
||||
{
|
||||
// draw a generic, no-name rainbow
|
||||
static uint8_t starthue = 0;
|
||||
fill_rainbow( leds + 5, NUM_LEDS - 5, --starthue, 20);
|
||||
|
||||
// Choose which 'color temperature' profile to enable.
|
||||
uint8_t secs = (millis() / 1000) % (DISPLAYTIME * 2);
|
||||
if( secs < DISPLAYTIME) {
|
||||
FastLED.setTemperature( TEMPERATURE_1 ); // first temperature
|
||||
leds[0] = TEMPERATURE_1; // show indicator pixel
|
||||
} else {
|
||||
FastLED.setTemperature( TEMPERATURE_2 ); // second temperature
|
||||
leds[0] = TEMPERATURE_2; // show indicator pixel
|
||||
}
|
||||
|
||||
// Black out the LEDs for a few secnds between color changes
|
||||
// to let the eyes and brains adjust
|
||||
if( (secs % DISPLAYTIME) < BLACKTIME) {
|
||||
memset8( leds, 0, NUM_LEDS * sizeof(CRGB));
|
||||
}
|
||||
|
||||
FastLED.show();
|
||||
FastLED.delay(8);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
delay( 3000 ); // power-up safety delay
|
||||
// It's important to set the color correction for your LED strip here,
|
||||
// so that colors can be more accurately rendered through the 'temperature' profiles
|
||||
FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalSMD5050 );
|
||||
FastLED.setBrightness( BRIGHTNESS );
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "FastLED.h"
|
||||
|
||||
void apollo3_tests() {
|
||||
#if FASTLED_USE_PROGMEM != 0
|
||||
#error "FASTLED_USE_PROGMEM should be 0 for Apollo3"
|
||||
#endif
|
||||
|
||||
#if SKETCH_HAS_LOTS_OF_MEMORY != 1
|
||||
#error "SKETCH_HAS_LOTS_OF_MEMORY should be 1 for Apollo3"
|
||||
#endif
|
||||
|
||||
#if FASTLED_ALLOW_INTERRUPTS != 1
|
||||
#error "FASTLED_ALLOW_INTERRUPTS should be 1 for Apollo3"
|
||||
#endif
|
||||
|
||||
#ifndef F_CPU
|
||||
#error "F_CPU should be defined for Apollo3"
|
||||
#endif
|
||||
|
||||
// Check that Apollo3-specific features are available
|
||||
#ifndef APOLLO3
|
||||
#warning "APOLLO3 macro not defined - this may indicate platform detection issues"
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#include "FastLED.h"
|
||||
|
||||
void arm_tests() {
|
||||
#ifndef FASTLED_ARM
|
||||
#error "FASTLED_ARM should be defined for ARM platforms"
|
||||
#endif
|
||||
|
||||
#if FASTLED_USE_PROGMEM != 0 && FASTLED_USE_PROGMEM != 1
|
||||
#error "FASTLED_USE_PROGMEM should be either 0 or 1 for ARM platforms"
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_TEENSYLC) || defined(ARDUINO_TEENSY30) || defined(__MK20DX128__) || defined(__MK20DX256__) || defined(ARDUINO_ARCH_RENESAS_UNO) || defined(STM32F1)
|
||||
// Teensy LC, Teensy 3.0, Teensy 3.1/3.2, Renesas UNO, and STM32F1 have limited memory
|
||||
#if SKETCH_HAS_LOTS_OF_MEMORY != 0
|
||||
#error "SKETCH_HAS_LOTS_OF_MEMORY should be 0 for Teensy LC, Teensy 3.0, Teensy 3.1/3.2, Renesas UNO, and STM32F1"
|
||||
#endif
|
||||
#else
|
||||
// Most other ARM platforms have lots of memory
|
||||
#if SKETCH_HAS_LOTS_OF_MEMORY != 1
|
||||
#error "SKETCH_HAS_LOTS_OF_MEMORY should be 1 for most ARM platforms"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if FASTLED_ALLOW_INTERRUPTS != 1 && FASTLED_ALLOW_INTERRUPTS != 0
|
||||
#error "FASTLED_ALLOW_INTERRUPTS should be either 0 or 1 for ARM platforms"
|
||||
#endif
|
||||
|
||||
// Check that F_CPU is defined
|
||||
#ifndef F_CPU
|
||||
#error "F_CPU should be defined for ARM platforms"
|
||||
#endif
|
||||
|
||||
// Specific ARM variant checks
|
||||
#if defined(ARDUINO_ARCH_STM32) || defined(STM32F1)
|
||||
#if FASTLED_ALLOW_INTERRUPTS != 0
|
||||
#error "STM32 platforms should have FASTLED_ALLOW_INTERRUPTS set to 0"
|
||||
#endif
|
||||
#if FASTLED_USE_PROGMEM != 0
|
||||
#error "STM32 platforms should have FASTLED_USE_PROGMEM set to 0"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_RASPBERRY_PI_PICO)
|
||||
#if FASTLED_USE_PROGMEM != 0
|
||||
#error "RP2040 platforms should have FASTLED_USE_PROGMEM set to 0"
|
||||
#endif
|
||||
#if FASTLED_ALLOW_INTERRUPTS != 1
|
||||
#error "RP2040 platforms should have FASTLED_ALLOW_INTERRUPTS set to 1"
|
||||
#endif
|
||||
#ifdef FASTLED_FORCE_SOFTWARE_SPI
|
||||
// RP2040 forces software SPI - this is expected
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__)
|
||||
// Teensy platforms that use PROGMEM
|
||||
#if FASTLED_USE_PROGMEM != 1
|
||||
#error "Teensy K20/K66/MXRT1062 platforms should have FASTLED_USE_PROGMEM set to 1"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_SAM_DUE)
|
||||
#if FASTLED_USE_PROGMEM != 0
|
||||
#error "SAMD/SAM platforms should have FASTLED_USE_PROGMEM set to 0"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(NRF52_SERIES) || defined(ARDUINO_ARCH_NRF52)
|
||||
#if FASTLED_USE_PROGMEM != 0
|
||||
#error "NRF52 platforms should have FASTLED_USE_PROGMEM set to 0"
|
||||
#endif
|
||||
#ifndef CLOCKLESS_FREQUENCY
|
||||
#error "NRF52 should have CLOCKLESS_FREQUENCY defined"
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "FastLED.h"
|
||||
|
||||
void avr_tests() {
|
||||
#if FASTLED_USE_PROGMEM != 1
|
||||
#error "FASTLED_USE_PROGMEM should be 1 for AVR"
|
||||
#endif
|
||||
|
||||
#if SKETCH_HAS_LOTS_OF_MEMORY != 0
|
||||
#error "SKETCH_HAS_LOTS_OF_MEMORY should be 0 for AVR"
|
||||
#endif
|
||||
|
||||
#if FASTLED_ALLOW_INTERRUPTS != 0
|
||||
#error "FASTLED_ALLOW_INTERRUPTS should be 0 for AVR (default)"
|
||||
#endif
|
||||
|
||||
#ifndef FASTLED_AVR
|
||||
#error "FASTLED_AVR should be defined for AVR platforms"
|
||||
#endif
|
||||
|
||||
#ifndef F_CPU
|
||||
#error "F_CPU should be defined for AVR platforms"
|
||||
#endif
|
||||
|
||||
// AVR should have a reasonable F_CPU (typically 8MHz or 16MHz)
|
||||
#if F_CPU < 1000000 || F_CPU > 32000000
|
||||
#warning "AVR F_CPU seems unusual - check if this is expected"
|
||||
#endif
|
||||
|
||||
// Check for Arduino environment on AVR
|
||||
#ifndef ARDUINO
|
||||
#warning "ARDUINO macro not defined - may not be in Arduino environment"
|
||||
#endif
|
||||
|
||||
// AVR-specific assembly optimizations should be enabled
|
||||
#ifndef QADD8_AVRASM
|
||||
#warning "AVR assembly optimizations may not be enabled"
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include "FastLED.h"
|
||||
|
||||
void wasm_tests() {
|
||||
#if FASTLED_USE_PROGMEM != 0
|
||||
#error "FASTLED_USE_PROGMEM should be 0 for WASM"
|
||||
#endif
|
||||
|
||||
#if SKETCH_HAS_LOTS_OF_MEMORY != 1
|
||||
#error "SKETCH_HAS_LOTS_OF_MEMORY should be 1 for WASM"
|
||||
#endif
|
||||
|
||||
#if FASTLED_ALLOW_INTERRUPTS != 1
|
||||
#error "FASTLED_ALLOW_INTERRUPTS should be 1 for WASM"
|
||||
#endif
|
||||
|
||||
#ifndef F_CPU
|
||||
#error "F_CPU should be defined for WASM"
|
||||
#endif
|
||||
|
||||
// WASM should have a high F_CPU since it's not real hardware
|
||||
#if F_CPU < 1000000
|
||||
#error "WASM F_CPU should be reasonably high (not real hardware constraint)"
|
||||
#endif
|
||||
|
||||
// Check WASM-specific defines
|
||||
#ifndef FASTLED_STUB_IMPL
|
||||
#error "FASTLED_STUB_IMPL should be defined for WASM"
|
||||
#endif
|
||||
|
||||
#ifndef digitalPinToBitMask
|
||||
#error "digitalPinToBitMask should be defined for WASM"
|
||||
#endif
|
||||
}
|
||||
183
.pio/libdeps/esp01_1m/FastLED/examples/Corkscrew/Corkscrew.h
Normal file
183
.pio/libdeps/esp01_1m/FastLED/examples/Corkscrew/Corkscrew.h
Normal file
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
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.
|
||||
|
||||
NEW: ScreenMap Support
|
||||
=====================
|
||||
You can now create a ScreenMap directly from a Corkscrew, which maps
|
||||
each LED index to its exact position on the cylindrical surface.
|
||||
This is useful for web interfaces and visualization:
|
||||
|
||||
Example usage:
|
||||
```cpp
|
||||
// Create a corkscrew
|
||||
Corkscrew corkscrew(totalTurns, numLeds);
|
||||
|
||||
// Create ScreenMap with 0.5cm LED diameter
|
||||
fl::ScreenMap screenMap = corkscrew.toScreenMap(0.5f);
|
||||
|
||||
// Use with FastLED controller for web visualization
|
||||
controller->setScreenMap(screenMap);
|
||||
```
|
||||
|
||||
NEW: Rectangular Buffer Support
|
||||
===============================
|
||||
You can now draw into a rectangular fl::Leds grid and read that
|
||||
into the corkscrew's internal buffer for display:
|
||||
|
||||
Example usage:
|
||||
```cpp
|
||||
// Create a rectangular grid to draw into
|
||||
CRGB grid_buffer[width * height];
|
||||
fl::Leds grid(grid_buffer, width, height);
|
||||
|
||||
// Draw your 2D patterns into the grid
|
||||
grid(x, y) = CRGB::Red; // etc.
|
||||
|
||||
// Draw patterns on the corkscrew's surface and map to LEDs
|
||||
auto& surface = corkscrew.surface();
|
||||
// Draw your patterns on the surface, then call draw() to map to LEDs
|
||||
corkscrew.draw();
|
||||
|
||||
// Access the LED pixel data
|
||||
auto ledData = corkscrew.data(); // Or corkscrew.rawData() for pointer
|
||||
// The LED data now contains the corkscrew mapping of your patterns
|
||||
```
|
||||
*/
|
||||
|
||||
#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_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);
|
||||
UICheckbox cachingEnabled("Enable Tile Caching", true);
|
||||
|
||||
// CRGB leds[NUM_LEDS];
|
||||
|
||||
// Tested on a 288 led (2x 144 max density led strip) with 19 turns
|
||||
// Auto-calculates optimal grid dimensions from turns and LEDs
|
||||
Corkscrew corkscrew(CORKSCREW_TURNS, // 19 turns
|
||||
NUM_LEDS); // 288 leds
|
||||
|
||||
// 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.cylinderWidth();
|
||||
int height = corkscrew.cylinderHeight();
|
||||
|
||||
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);
|
||||
|
||||
// NEW: Create ScreenMap directly from Corkscrew using toScreenMap()
|
||||
// This maps each LED index to its exact position on the cylindrical surface
|
||||
fl::ScreenMap corkscrewScreenMap = corkscrew.toScreenMap(0.2f);
|
||||
|
||||
// Alternative: Create ScreenMap from rectangular XYMap (old way)
|
||||
// fl::ScreenMap screenMap = xyMap.toScreenMap();
|
||||
// screenMap.setDiameter(.2f);
|
||||
|
||||
// Set the corkscrew screen map for the controller
|
||||
// This allows the web interface to display the actual corkscrew shape
|
||||
controller->setScreenMap(corkscrewScreenMap);
|
||||
|
||||
// Initialize caching based on UI setting
|
||||
corkscrew.setCachingEnabled(cachingEnabled.value());
|
||||
}
|
||||
|
||||
void loop() {
|
||||
fl::clear(frameBuffer);
|
||||
static float pos = 0;
|
||||
pos += speed.value();
|
||||
if (pos > corkscrew.size() - 1) {
|
||||
pos = 0; // Reset to the beginning
|
||||
}
|
||||
|
||||
// Update caching setting if it changed
|
||||
static bool lastCachingState = cachingEnabled.value();
|
||||
if (lastCachingState != cachingEnabled.value()) {
|
||||
corkscrew.setCachingEnabled(cachingEnabled.value());
|
||||
lastCachingState = cachingEnabled.value();
|
||||
}
|
||||
|
||||
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);
|
||||
vec2<u16> 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_no_wrap(pos);
|
||||
vec2<u16> pos_i16 = vec2<u16>(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();
|
||||
}
|
||||
@@ -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
|
||||
60
.pio/libdeps/esp01_1m/FastLED/examples/Cylon/Cylon.ino
Normal file
60
.pio/libdeps/esp01_1m/FastLED/examples/Cylon/Cylon.ino
Normal file
@@ -0,0 +1,60 @@
|
||||
/// @file Cylon.ino
|
||||
/// @brief An animation that moves a single LED back and forth as the entire strip changes.
|
||||
/// (Larson Scanner effect)
|
||||
/// @example Cylon.ino
|
||||
|
||||
#include <FastLED.h>
|
||||
|
||||
using namespace fl;
|
||||
|
||||
// How many leds in your strip?
|
||||
#define NUM_LEDS 64
|
||||
|
||||
// For led chips like Neopixels, which have a data line, ground, and power, you just
|
||||
// need to define DATA_PIN. For led chipsets that are SPI based (four wires - data, clock,
|
||||
// ground, and power), like the LPD8806, define both DATA_PIN and CLOCK_PIN
|
||||
#define DATA_PIN 2
|
||||
#define CLOCK_PIN 13
|
||||
|
||||
// Define the array of leds
|
||||
CRGB leds[NUM_LEDS];
|
||||
|
||||
void setup() {
|
||||
Serial.begin(57600);
|
||||
Serial.println("resetting");
|
||||
FastLED.addLeds<WS2812,DATA_PIN,RGB>(leds,NUM_LEDS);
|
||||
FastLED.setBrightness(84);
|
||||
}
|
||||
|
||||
void fadeall() { for(int i = 0; i < NUM_LEDS; i++) { leds[i].nscale8(250); } }
|
||||
|
||||
void loop() {
|
||||
static uint8_t hue = 0;
|
||||
Serial.print("x");
|
||||
// First slide the led in one direction
|
||||
for(int i = 0; i < NUM_LEDS; i++) {
|
||||
// Set the i'th led to red
|
||||
leds[i] = CHSV(hue++, 255, 255);
|
||||
// Show the leds
|
||||
FastLED.show();
|
||||
// now that we've shown the leds, reset the i'th led to black
|
||||
// leds[i] = CRGB::Black;
|
||||
fadeall();
|
||||
// Wait a little bit before we loop around and do it again
|
||||
delay(10);
|
||||
}
|
||||
Serial.print("x");
|
||||
|
||||
// Now go in the other direction.
|
||||
for(int i = (NUM_LEDS)-1; i >= 0; i--) {
|
||||
// Set the i'th led to red
|
||||
leds[i] = CHSV(hue++, 255, 255);
|
||||
// Show the leds
|
||||
FastLED.show();
|
||||
// now that we've shown the leds, reset the i'th led to black
|
||||
// leds[i] = CRGB::Black;
|
||||
fadeall();
|
||||
// Wait a little bit before we loop around and do it again
|
||||
delay(10);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/// @file DemoReel100.ino
|
||||
/// @brief FastLED "100 lines of code" demo reel, showing off some effects
|
||||
/// @example DemoReel100.ino
|
||||
|
||||
#include <FastLED.h>
|
||||
|
||||
// FastLED "100-lines-of-code" demo reel, showing just a few
|
||||
// of the kinds of animation patterns you can quickly and easily
|
||||
// compose using FastLED.
|
||||
//
|
||||
// This example also shows one easy way to define multiple
|
||||
// animations patterns and have them automatically rotate.
|
||||
//
|
||||
// -Mark Kriegsman, December 2014
|
||||
|
||||
|
||||
#define DATA_PIN 3
|
||||
//#define CLK_PIN 4
|
||||
#define LED_TYPE WS2811
|
||||
#define COLOR_ORDER GRB
|
||||
#define NUM_LEDS 64
|
||||
CRGB leds[NUM_LEDS];
|
||||
|
||||
#define BRIGHTNESS 96
|
||||
#define FRAMES_PER_SECOND 120
|
||||
|
||||
void setup() {
|
||||
delay(3000); // 3 second delay for recovery
|
||||
|
||||
// tell FastLED about the LED strip configuration
|
||||
FastLED.addLeds<LED_TYPE,DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
|
||||
//FastLED.addLeds<LED_TYPE,DATA_PIN,CLK_PIN,COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
|
||||
|
||||
// set master brightness control
|
||||
FastLED.setBrightness(BRIGHTNESS);
|
||||
}
|
||||
|
||||
// Forward declarations for pattern functions
|
||||
void rainbow();
|
||||
void rainbowWithGlitter();
|
||||
void confetti();
|
||||
void sinelon();
|
||||
void juggle();
|
||||
void bpm();
|
||||
void nextPattern();
|
||||
void addGlitter(fract8 chanceOfGlitter);
|
||||
|
||||
// List of patterns to cycle through. Each is defined as a separate function below.
|
||||
typedef void (*SimplePatternList[])();
|
||||
SimplePatternList gPatterns = { rainbow, rainbowWithGlitter, confetti, sinelon, juggle, bpm };
|
||||
|
||||
uint8_t gCurrentPatternNumber = 0; // Index number of which pattern is current
|
||||
uint8_t gHue = 0; // rotating "base color" used by many of the patterns
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Call the current pattern function once, updating the 'leds' array
|
||||
gPatterns[gCurrentPatternNumber]();
|
||||
|
||||
// send the 'leds' array out to the actual LED strip
|
||||
FastLED.show();
|
||||
// insert a delay to keep the framerate modest
|
||||
FastLED.delay(1000/FRAMES_PER_SECOND);
|
||||
|
||||
// do some periodic updates
|
||||
EVERY_N_MILLISECONDS( 20 ) { gHue++; } // slowly cycle the "base color" through the rainbow
|
||||
EVERY_N_SECONDS( 10 ) { nextPattern(); } // change patterns periodically
|
||||
}
|
||||
|
||||
#define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0]))
|
||||
|
||||
void nextPattern()
|
||||
{
|
||||
// add one to the current pattern number, and wrap around at the end
|
||||
gCurrentPatternNumber = (gCurrentPatternNumber + 1) % ARRAY_SIZE( gPatterns);
|
||||
}
|
||||
|
||||
void rainbow()
|
||||
{
|
||||
// FastLED's built-in rainbow generator
|
||||
fill_rainbow( leds, NUM_LEDS, gHue, 7);
|
||||
}
|
||||
|
||||
void rainbowWithGlitter()
|
||||
{
|
||||
// built-in FastLED rainbow, plus some random sparkly glitter
|
||||
rainbow();
|
||||
addGlitter(80);
|
||||
}
|
||||
|
||||
void addGlitter( fract8 chanceOfGlitter)
|
||||
{
|
||||
if( random8() < chanceOfGlitter) {
|
||||
leds[ random16(NUM_LEDS) ] += CRGB::White;
|
||||
}
|
||||
}
|
||||
|
||||
void confetti()
|
||||
{
|
||||
// random colored speckles that blink in and fade smoothly
|
||||
fadeToBlackBy( leds, NUM_LEDS, 10);
|
||||
int pos = random16(NUM_LEDS);
|
||||
leds[pos] += CHSV( gHue + random8(64), 200, 255);
|
||||
}
|
||||
|
||||
void sinelon()
|
||||
{
|
||||
// a colored dot sweeping back and forth, with fading trails
|
||||
fadeToBlackBy( leds, NUM_LEDS, 20);
|
||||
int pos = beatsin16( 13, 0, NUM_LEDS-1 );
|
||||
leds[pos] += CHSV( gHue, 255, 192);
|
||||
}
|
||||
|
||||
void bpm()
|
||||
{
|
||||
// colored stripes pulsing at a defined Beats-Per-Minute (BPM)
|
||||
uint8_t BeatsPerMinute = 62;
|
||||
CRGBPalette16 palette = PartyColors_p;
|
||||
uint8_t beat = beatsin8( BeatsPerMinute, 64, 255);
|
||||
for( int i = 0; i < NUM_LEDS; i++) { //9948
|
||||
leds[i] = ColorFromPalette(palette, gHue+(i*2), beat-gHue+(i*10));
|
||||
}
|
||||
}
|
||||
|
||||
void juggle() {
|
||||
// eight colored dots, weaving in and out of sync with each other
|
||||
fadeToBlackBy( leds, NUM_LEDS, 20);
|
||||
uint8_t dothue = 0;
|
||||
for( int i = 0; i < 8; i++) {
|
||||
leds[beatsin16( i+7, 0, NUM_LEDS-1 )] |= CHSV(dothue, 200, 255);
|
||||
dothue += 32;
|
||||
}
|
||||
}
|
||||
218
.pio/libdeps/esp01_1m/FastLED/examples/Downscale/Downscale.h
Normal file
218
.pio/libdeps/esp01_1m/FastLED/examples/Downscale/Downscale.h
Normal file
@@ -0,0 +1,218 @@
|
||||
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <FastLED.h>
|
||||
|
||||
#include "fl/downscale.h"
|
||||
#include "fl/draw_visitor.h"
|
||||
#include "fl/math_macros.h"
|
||||
#include "fl/raster.h"
|
||||
#include "fl/time_alpha.h"
|
||||
#include "fl/ui.h"
|
||||
#include "fl/xypath.h"
|
||||
#include "fx/time.h"
|
||||
|
||||
// Sketch.
|
||||
#include "src/wave.h"
|
||||
#include "src/xypaths.h"
|
||||
|
||||
using namespace fl;
|
||||
|
||||
#define HEIGHT 64
|
||||
#define WIDTH 64
|
||||
#define NUM_LEDS ((WIDTH) * (HEIGHT))
|
||||
#define TIME_ANIMATION 1000 // ms
|
||||
|
||||
CRGB leds[NUM_LEDS];
|
||||
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.
|
||||
// XYPathPtr shape = XYPath::NewRosePath(WIDTH, HEIGHT);
|
||||
|
||||
// Speed up writing to the super sampled waveFx by writing
|
||||
// to a raster. This will allow duplicate writes to be removed.
|
||||
|
||||
WaveEffect wave_fx; // init in setup().
|
||||
fl::vector<XYPathPtr> shapes = CreateXYPaths(WIDTH, HEIGHT);
|
||||
|
||||
XYRaster raster(WIDTH, HEIGHT);
|
||||
TimeWarp time_warp;
|
||||
|
||||
XYPathPtr getShape(int which) {
|
||||
int len = shapes.size();
|
||||
which = which % len;
|
||||
if (which < 0) {
|
||||
which += len;
|
||||
}
|
||||
return shapes[which];
|
||||
}
|
||||
|
||||
//////////////////// UI Section /////////////////////////////
|
||||
UITitle title("XYPath Demo");
|
||||
UIDescription description("Use a path on the WaveFx");
|
||||
UIButton trigger("Trigger");
|
||||
UISlider whichShape("Which Shape", 0.0f, 0.0f, shapes.size() - 1, 1.0f);
|
||||
UICheckbox useWaveFx("Use WaveFX", true);
|
||||
UISlider transition("Transition", 0.0f, 0.0f, 1.0f, 0.01f);
|
||||
|
||||
UISlider scale("Scale", 1.0f, 0.0f, 1.0f, 0.01f);
|
||||
UISlider speed("Speed", 1.0f, -20.0f, 20.0f, 0.01f);
|
||||
UISlider numberOfSteps("Number of Steps", 32.0f, 1.0f, 100.0f, 1.0f);
|
||||
UISlider maxAnimation("Max Animation", 1.0f, 5.0f, 20.0f, 1.f);
|
||||
|
||||
TimeClampedTransition shapeProgress(TIME_ANIMATION);
|
||||
|
||||
void setupUiCallbacks() {
|
||||
speed.onChanged([](UISlider& slider) {
|
||||
time_warp.setSpeed(slider.value());
|
||||
});
|
||||
maxAnimation.onChanged(
|
||||
[](UISlider& slider) {
|
||||
shapeProgress.set_max_clamp(slider.value());
|
||||
});
|
||||
|
||||
trigger.onClicked([]() {
|
||||
// shapeProgress.trigger(millis());
|
||||
FASTLED_WARN("Trigger pressed");
|
||||
});
|
||||
useWaveFx.onChanged([](fl::UICheckbox &checkbox) {
|
||||
if (checkbox.value()) {
|
||||
FASTLED_WARN("WaveFX enabled");
|
||||
} else {
|
||||
FASTLED_WARN("WaveFX disabled");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
auto screenmap = xyMap.toScreenMap();
|
||||
screenmap.setDiameter(.2);
|
||||
FastLED.addLeds<NEOPIXEL, 2>(leds, xyMap.getTotal())
|
||||
.setScreenMap(screenmap);
|
||||
auto screenmap2 = xyMap_Dst.toScreenMap();
|
||||
screenmap.setDiameter(.5);
|
||||
screenmap2.addOffsetY(-HEIGHT / 2);
|
||||
FastLED.addLeds<NEOPIXEL, 3>(leds_downscaled, xyMap_Dst.getTotal())
|
||||
.setScreenMap(screenmap2);
|
||||
setupUiCallbacks();
|
||||
// Initialize wave simulation. Please don't use static constructors, keep it
|
||||
// in setup().
|
||||
trigger.click();
|
||||
wave_fx = NewWaveSimulation2D(xyMap);
|
||||
}
|
||||
|
||||
//////////////////// LOOP SECTION /////////////////////////////
|
||||
|
||||
float getAnimationTime(uint32_t now) {
|
||||
float pointf = shapeProgress.updatef(now);
|
||||
return pointf + transition.value();
|
||||
}
|
||||
|
||||
void clearLeds() {
|
||||
fl::clear(leds);
|
||||
fl::clear(leds_downscaled);
|
||||
};
|
||||
|
||||
void loop() {
|
||||
// Your code here
|
||||
clearLeds();
|
||||
const uint32_t now = millis();
|
||||
uint32_t now_warped = time_warp.update(now);
|
||||
|
||||
auto shape = getShape(whichShape.as<int>());
|
||||
shape->setScale(scale.value());
|
||||
|
||||
float curr_alpha = getAnimationTime(now_warped);
|
||||
static float s_prev_alpha = 0.0f;
|
||||
|
||||
// unconditionally apply the circle.
|
||||
if (trigger) {
|
||||
// trigger the transition
|
||||
time_warp.reset(now);
|
||||
now_warped = time_warp.update(now);
|
||||
shapeProgress.trigger(now_warped);
|
||||
FASTLED_WARN("Transition triggered on " << shape->name());
|
||||
curr_alpha = getAnimationTime(now_warped);
|
||||
s_prev_alpha = curr_alpha;
|
||||
}
|
||||
|
||||
clearLeds();
|
||||
const CRGB purple = CRGB(255, 0, 255);
|
||||
const int number_of_steps = numberOfSteps.value();
|
||||
raster.reset();
|
||||
|
||||
float diff = curr_alpha - s_prev_alpha;
|
||||
diff *= 1.0f;
|
||||
float factor = MAX(s_prev_alpha - diff, 0.f);
|
||||
|
||||
for (int i = 0; i < number_of_steps; ++i) {
|
||||
float a =
|
||||
fl::map_range<float>(i, 0, number_of_steps - 1, factor, curr_alpha);
|
||||
if (a < .04) {
|
||||
// shorter tails at first.
|
||||
a = map_range<float>(a, 0.0f, .04f, 0.0f, .04f);
|
||||
}
|
||||
float diff_max_alpha = maxAnimation.value() - curr_alpha;
|
||||
if (diff_max_alpha < 0.94) {
|
||||
// shorter tails at the end.
|
||||
a = map_range<float>(a, curr_alpha, maxAnimation.value(),
|
||||
curr_alpha, maxAnimation.value());
|
||||
}
|
||||
uint8_t alpha =
|
||||
fl::map_range<uint8_t>(i, 0.0f, number_of_steps - 1, 64, 255);
|
||||
Tile2x2_u8 subpixel = shape->at_subpixel(a);
|
||||
subpixel.scale(alpha);
|
||||
// subpixels.push_back(subpixel);
|
||||
raster.rasterize(subpixel);
|
||||
}
|
||||
|
||||
s_prev_alpha = curr_alpha;
|
||||
|
||||
if (useWaveFx) {
|
||||
DrawRasterToWaveSimulator draw_wave_fx(&wave_fx);
|
||||
raster.draw(xyMap, draw_wave_fx);
|
||||
} else {
|
||||
raster.draw(purple, xyMap, leds);
|
||||
}
|
||||
|
||||
int first = xyMap(1, 1);
|
||||
int last = xyMap(WIDTH - 2, HEIGHT - 2);
|
||||
|
||||
leds[first] = CRGB(255, 0, 0);
|
||||
leds[last] = CRGB(0, 255, 0);
|
||||
if (useWaveFx) {
|
||||
// fxBlend.draw(Fx::DrawContext(now, leds));
|
||||
wave_fx.draw(Fx::DrawContext(now, leds));
|
||||
}
|
||||
|
||||
// downscaleBilinear(leds, WIDTH, HEIGHT, leds_downscaled, WIDTH / 2,
|
||||
// HEIGHT / 2);
|
||||
|
||||
downscaleHalf(leds, xyMap, leds_downscaled, xyMap_Dst);
|
||||
|
||||
// Print out the first 10 pixels of the original and downscaled
|
||||
fl::vector_inlined<CRGB, 10> downscaled_pixels;
|
||||
fl::vector_inlined<CRGB, 10> original_pixels;
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
original_pixels.push_back(leds[i]);
|
||||
downscaled_pixels.push_back(leds_downscaled[i]);
|
||||
}
|
||||
|
||||
FastLED.show();
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <FastLED.h>
|
||||
|
||||
#if !SKETCH_HAS_LOTS_OF_MEMORY
|
||||
// Platform does not have enough memory
|
||||
void setup() {
|
||||
Serial.println("setup");
|
||||
}
|
||||
void loop() {
|
||||
Serial.println("Downscale disabled");
|
||||
delay(1000);
|
||||
}
|
||||
#else
|
||||
|
||||
#include "Downscale.h"
|
||||
|
||||
#endif // SKETCH_HAS_LOTS_OF_MEMORY
|
||||
@@ -0,0 +1,65 @@
|
||||
|
||||
#include "wave.h"
|
||||
#include "FastLED.h"
|
||||
|
||||
DEFINE_GRADIENT_PALETTE(electricBlueFirePal){
|
||||
0, 0, 0, 0, // Black
|
||||
32, 0, 0, 70, // Dark blue
|
||||
128, 20, 57, 255, // Electric blue
|
||||
255, 255, 255, 255 // White
|
||||
};
|
||||
|
||||
DEFINE_GRADIENT_PALETTE(electricGreenFirePal){
|
||||
0, 0, 0, 0, // black
|
||||
8, 128, 64, 64, // green
|
||||
16, 255, 222, 222, // red
|
||||
64, 255, 255, 255, // white
|
||||
255, 255, 255, 255 // white
|
||||
};
|
||||
|
||||
WaveFx::Args CreateArgsLower() {
|
||||
WaveFx::Args out;
|
||||
out.factor = SuperSample::SUPER_SAMPLE_2X;
|
||||
out.half_duplex = true;
|
||||
out.auto_updates = true;
|
||||
out.speed = 0.18f;
|
||||
out.dampening = 9.0f;
|
||||
out.crgbMap = fl::make_shared<WaveCrgbGradientMap>(electricBlueFirePal);
|
||||
return out;
|
||||
}
|
||||
|
||||
WaveFx::Args CreateArgsUpper() {
|
||||
WaveFx::Args out;
|
||||
out.factor = SuperSample::SUPER_SAMPLE_2X;
|
||||
out.half_duplex = true;
|
||||
out.auto_updates = true;
|
||||
out.speed = 0.25f;
|
||||
out.dampening = 3.0f;
|
||||
out.crgbMap = fl::make_shared<WaveCrgbGradientMap>(electricGreenFirePal);
|
||||
return out;
|
||||
}
|
||||
|
||||
WaveEffect NewWaveSimulation2D(const XYMap& xymap) {
|
||||
// only apply complex xymap as the last step after compositiing.
|
||||
XYMap xy_rect =
|
||||
XYMap::constructRectangularGrid(xymap.getWidth(), xymap.getHeight());
|
||||
Blend2dPtr fxBlend =
|
||||
fl::make_shared<Blend2d>(xymap); // Final transformation goes to the blend stack.
|
||||
int width = xymap.getWidth();
|
||||
int height = xymap.getHeight();
|
||||
XYMap xyRect(width, height, false);
|
||||
WaveFx::Args args_lower = CreateArgsLower();
|
||||
WaveFx::Args args_upper = CreateArgsUpper();
|
||||
WaveFxPtr wave_fx_low = fl::make_shared<WaveFx>(xy_rect, args_lower);
|
||||
WaveFxPtr wave_fx_high = fl::make_shared<WaveFx>(xy_rect, args_upper);
|
||||
Blend2dPtr blend_stack = fl::make_shared<Blend2d>(xymap);
|
||||
blend_stack->add(wave_fx_low);
|
||||
blend_stack->add(wave_fx_high);
|
||||
WaveEffect out = {
|
||||
.wave_fx_low = wave_fx_low,
|
||||
.wave_fx_high = wave_fx_high,
|
||||
.blend_stack = blend_stack,
|
||||
};
|
||||
|
||||
return out;
|
||||
}
|
||||
34
.pio/libdeps/esp01_1m/FastLED/examples/Downscale/src/wave.h
Normal file
34
.pio/libdeps/esp01_1m/FastLED/examples/Downscale/src/wave.h
Normal file
@@ -0,0 +1,34 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fx/2d/blend.h"
|
||||
#include "fx/2d/wave.h"
|
||||
#include "fx/fx2d.h"
|
||||
#include "fl/raster.h"
|
||||
|
||||
using namespace fl;
|
||||
|
||||
struct WaveEffect {
|
||||
WaveFxPtr wave_fx_low;
|
||||
WaveFxPtr wave_fx_high;
|
||||
Blend2dPtr blend_stack;
|
||||
void draw(Fx::DrawContext context) { blend_stack->draw(context); }
|
||||
void addf(size_t x, size_t y, float value) {
|
||||
wave_fx_low->addf(x, y, value);
|
||||
wave_fx_high->addf(x, y, value);
|
||||
}
|
||||
};
|
||||
|
||||
struct DrawRasterToWaveSimulator {
|
||||
DrawRasterToWaveSimulator(WaveEffect* wave_fx) : mWaveFx(wave_fx) {}
|
||||
void draw(const vec2<uint16_t> &pt, uint32_t /*index*/, uint8_t value) {
|
||||
float valuef = value / 255.0f;
|
||||
size_t xx = pt.x;
|
||||
size_t yy = pt.y;
|
||||
mWaveFx->addf(xx, yy, valuef);
|
||||
}
|
||||
WaveEffect* mWaveFx;
|
||||
};
|
||||
|
||||
WaveEffect NewWaveSimulation2D(const XYMap& xymap);
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
|
||||
#include "fl/xypath.h"
|
||||
#include "fl/vector.h"
|
||||
#include "fl/map_range.h"
|
||||
|
||||
|
||||
#include "xypaths.h"
|
||||
|
||||
using namespace fl;
|
||||
|
||||
namespace {
|
||||
fl::shared_ptr<CatmullRomParams> make_path(int width, int height) {
|
||||
// make a triangle.
|
||||
fl::shared_ptr<CatmullRomParams> params = fl::make_shared<CatmullRomParams>();
|
||||
vector_inlined<vec2f, 5> points;
|
||||
points.push_back(vec2f(0.0f, 0.0f));
|
||||
points.push_back(vec2f(width / 3, height / 2));
|
||||
points.push_back(vec2f(width - 3, height - 1));
|
||||
points.push_back(vec2f(0.0f, height - 1));
|
||||
points.push_back(vec2f(0.0f, 0.0f));
|
||||
for (auto &p : points) {
|
||||
p.x = map_range<float, float>(p.x, 0.0f, width - 1, -1.0f, 1.0f);
|
||||
p.y = map_range<float, float>(p.y, 0.0f, height - 1, -1.0f, 1.0f);
|
||||
params->addPoint(p);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
}
|
||||
|
||||
fl::vector<XYPathPtr> CreateXYPaths(int width, int height) {
|
||||
fl::vector<XYPathPtr> out;
|
||||
out.push_back(XYPath::NewCirclePath(width, height));
|
||||
out.push_back(XYPath::NewRosePath(width, height));
|
||||
out.push_back(XYPath::NewHeartPath(width, height));
|
||||
out.push_back(XYPath::NewArchimedeanSpiralPath(width, height));
|
||||
out.push_back(XYPath::NewPhyllotaxisPath(width, height));
|
||||
out.push_back(XYPath::NewGielisCurvePath(width, height));
|
||||
out.push_back(XYPath::NewCatmullRomPath(width, height, make_path(width, height)));
|
||||
return out;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
|
||||
#include "fl/xypath.h"
|
||||
#include "fl/vector.h"
|
||||
|
||||
using namespace fl;
|
||||
|
||||
// XYPath::NewRosePath(WIDTH, HEIGHT);
|
||||
|
||||
fl::vector<XYPathPtr> CreateXYPaths(int width, int height);
|
||||
129
.pio/libdeps/esp01_1m/FastLED/examples/EaseInOut/EaseInOut.h
Normal file
129
.pio/libdeps/esp01_1m/FastLED/examples/EaseInOut/EaseInOut.h
Normal file
@@ -0,0 +1,129 @@
|
||||
/// @file EaseInOut.ino
|
||||
/// @brief Demonstrates easing functions with visual curve display
|
||||
/// @example EaseInOut.ino
|
||||
///
|
||||
/// 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`
|
||||
/// 4. When the compiler is done a web page will open.
|
||||
|
||||
#include <FastLED.h>
|
||||
#include "fl/ease.h"
|
||||
#include "fl/leds.h"
|
||||
|
||||
using namespace fl;
|
||||
|
||||
// Matrix configuration
|
||||
#define MATRIX_WIDTH 100
|
||||
#define MATRIX_HEIGHT 100
|
||||
#define NUM_LEDS (MATRIX_WIDTH * MATRIX_HEIGHT)
|
||||
#define DATA_PIN 3
|
||||
#define LED_TYPE WS2812B
|
||||
#define COLOR_ORDER GRB
|
||||
#define BRIGHTNESS 255
|
||||
|
||||
|
||||
#define MATRIX_SERPENTINE true
|
||||
|
||||
// Use LedsXY for splat rendering instead of regular CRGB array
|
||||
LedsXY<MATRIX_WIDTH, MATRIX_HEIGHT> leds;
|
||||
|
||||
// Create XYMap for serpentine 100x100 matrix
|
||||
XYMap xyMap = XYMap::constructSerpentine(MATRIX_WIDTH, MATRIX_HEIGHT);
|
||||
|
||||
UITitle title("EaseInOut");
|
||||
UIDescription description("Use the xPosition slider to see the ease function curve. Use the Ease Type dropdown to select different easing functions. Use the 16-bit checkbox to toggle between 16-bit (checked) and 8-bit (unchecked) precision.");
|
||||
|
||||
// UI Controls
|
||||
UISlider xPosition("xPosition", 0.0f, 0.0f, 1.0f, 0.01f);
|
||||
|
||||
// Create dropdown with descriptive ease function names
|
||||
fl::string easeOptions[] = {
|
||||
"None",
|
||||
"In Quad",
|
||||
"Out Quad",
|
||||
"In-Out Quad",
|
||||
"In Cubic",
|
||||
"Out Cubic",
|
||||
"In-Out Cubic",
|
||||
"In Sine",
|
||||
"Out Sine",
|
||||
"In-Out Sine"
|
||||
};
|
||||
UIDropdown easeTypeDropdown("Ease Type", easeOptions);
|
||||
|
||||
UICheckbox use16Bit("16-bit", true); // Default checked for 16-bit precision
|
||||
|
||||
EaseType getEaseType(int value) {
|
||||
switch (value) {
|
||||
case 0: return EASE_NONE;
|
||||
case 1: return EASE_IN_QUAD;
|
||||
case 2: return EASE_OUT_QUAD;
|
||||
case 3: return EASE_IN_OUT_QUAD;
|
||||
case 4: return EASE_IN_CUBIC;
|
||||
case 5: return EASE_OUT_CUBIC;
|
||||
case 6: return EASE_IN_OUT_CUBIC;
|
||||
case 7: return EASE_IN_SINE;
|
||||
case 8: return EASE_OUT_SINE;
|
||||
case 9: return EASE_IN_OUT_SINE;
|
||||
}
|
||||
FL_ASSERT(false, "Invalid ease type");
|
||||
return EASE_IN_OUT_QUAD;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("FastLED Ease16InOutQuad Demo - Simple Curve Visualization");
|
||||
|
||||
// Add LEDs and set screen map
|
||||
auto *controller =
|
||||
&FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS);
|
||||
|
||||
// Convert XYMap to ScreenMap and set it on the controller
|
||||
fl::ScreenMap screenMap = xyMap.toScreenMap();
|
||||
screenMap.setDiameter(.5); // Set LED diameter for visualization
|
||||
controller->setScreenMap(screenMap);
|
||||
|
||||
// Configure FastLED
|
||||
FastLED.setBrightness(BRIGHTNESS);
|
||||
FastLED.setCorrection(TypicalLEDStrip);
|
||||
FastLED.setDither(BRIGHTNESS < 255);
|
||||
|
||||
// Set default dropdown selection to "In-Out Quad" (index 3)
|
||||
easeTypeDropdown.setSelectedIndex(3);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Clear the matrix using fl::clear for LedsXY
|
||||
fl::clear(leds);
|
||||
|
||||
// Get the current slider value (0.0 to 1.0)
|
||||
float sliderValue = xPosition.value();
|
||||
|
||||
// Map slider value to X coordinate (0 to width-1)
|
||||
uint8_t x = map(sliderValue * 1000, 0, 1000, 0, MATRIX_WIDTH - 1);
|
||||
|
||||
// Get the selected ease type using the dropdown index
|
||||
EaseType selectedEaseType = getEaseType(easeTypeDropdown.as_int());
|
||||
|
||||
uint8_t y;
|
||||
if (use16Bit.value()) {
|
||||
// Use 16-bit precision
|
||||
uint16_t easeInput = map(sliderValue * 1000, 0, 1000, 0, 65535);
|
||||
uint16_t easeOutput = ease16(selectedEaseType, easeInput);
|
||||
y = map(easeOutput, 0, 65535, 0, MATRIX_HEIGHT - 1);
|
||||
} else {
|
||||
// Use 8-bit precision
|
||||
uint8_t easeInput = map(sliderValue * 1000, 0, 1000, 0, 255);
|
||||
uint8_t easeOutput = ease8(selectedEaseType, easeInput);
|
||||
y = map(easeOutput, 0, 255, 0, MATRIX_HEIGHT - 1);
|
||||
}
|
||||
|
||||
// Draw white dot at the calculated position using splat rendering
|
||||
if (x < MATRIX_WIDTH && y < MATRIX_HEIGHT) {
|
||||
leds(x, y) = CRGB::White;
|
||||
}
|
||||
|
||||
FastLED.show();
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#include "fl/sketch_macros.h"
|
||||
|
||||
#if !SKETCH_HAS_LOTS_OF_MEMORY
|
||||
// Platform does not have enough memory
|
||||
void setup() {}
|
||||
void loop() {}
|
||||
#else
|
||||
#include "EaseInOut.h"
|
||||
#endif
|
||||
@@ -0,0 +1,205 @@
|
||||
|
||||
#ifdef ESP32
|
||||
|
||||
/// 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.
|
||||
/// - 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 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 working, remove the printfs and see if that fixes the problem.
|
||||
///
|
||||
/// Is RGBW supported? Yes.
|
||||
///
|
||||
/// 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? Yes, they have 250us timing.
|
||||
///
|
||||
/// 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/54.03.20/platform-espressif32.zip
|
||||
// framework = arduino
|
||||
// board = seeed_xiao_esp32s3
|
||||
|
||||
|
||||
#define FASTLED_USES_ESP32S3_I2S // Must define this before including FastLED.h
|
||||
|
||||
|
||||
#include "FastLED.h"
|
||||
#include "fl/assert.h"
|
||||
|
||||
|
||||
#define NUMSTRIPS 16
|
||||
#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
|
||||
#define EXAMPLE_PIN_NUM_DATA3 6 // B3
|
||||
#define EXAMPLE_PIN_NUM_DATA4 7 // B4
|
||||
#define EXAMPLE_PIN_NUM_DATA5 8 // G0
|
||||
#define EXAMPLE_PIN_NUM_DATA6 9 // G1
|
||||
#define EXAMPLE_PIN_NUM_DATA7 10 // G2
|
||||
#define EXAMPLE_PIN_NUM_DATA8 11 // G3
|
||||
#define EXAMPLE_PIN_NUM_DATA9 12 // G4
|
||||
#define EXAMPLE_PIN_NUM_DATA10 13 // G5
|
||||
#define EXAMPLE_PIN_NUM_DATA11 14 // R0
|
||||
#define EXAMPLE_PIN_NUM_DATA12 15 // R1
|
||||
#define EXAMPLE_PIN_NUM_DATA13 16 // R2
|
||||
#define EXAMPLE_PIN_NUM_DATA14 17 // R3
|
||||
#define EXAMPLE_PIN_NUM_DATA15 18 // R4
|
||||
|
||||
|
||||
// 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,
|
||||
EXAMPLE_PIN_NUM_DATA2,
|
||||
EXAMPLE_PIN_NUM_DATA3,
|
||||
EXAMPLE_PIN_NUM_DATA4,
|
||||
EXAMPLE_PIN_NUM_DATA5,
|
||||
EXAMPLE_PIN_NUM_DATA6,
|
||||
EXAMPLE_PIN_NUM_DATA7,
|
||||
EXAMPLE_PIN_NUM_DATA8,
|
||||
EXAMPLE_PIN_NUM_DATA9,
|
||||
EXAMPLE_PIN_NUM_DATA10,
|
||||
EXAMPLE_PIN_NUM_DATA11,
|
||||
EXAMPLE_PIN_NUM_DATA12,
|
||||
EXAMPLE_PIN_NUM_DATA13,
|
||||
EXAMPLE_PIN_NUM_DATA14,
|
||||
EXAMPLE_PIN_NUM_DATA15
|
||||
};
|
||||
|
||||
CRGB leds[NUM_LEDS];
|
||||
|
||||
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>(
|
||||
leds + (0 * NUM_LEDS_PER_STRIP), NUM_LEDS_PER_STRIP
|
||||
);
|
||||
FastLED.addLeds<WS2812, EXAMPLE_PIN_NUM_DATA1, GRB>(
|
||||
leds + (1 * NUM_LEDS_PER_STRIP), NUM_LEDS_PER_STRIP
|
||||
);
|
||||
FastLED.addLeds<WS2812, EXAMPLE_PIN_NUM_DATA2, GRB>(
|
||||
leds + (2 * NUM_LEDS_PER_STRIP), NUM_LEDS_PER_STRIP
|
||||
);
|
||||
FastLED.addLeds<WS2812, EXAMPLE_PIN_NUM_DATA3, GRB>(
|
||||
leds + (3 * NUM_LEDS_PER_STRIP), NUM_LEDS_PER_STRIP
|
||||
);
|
||||
FastLED.addLeds<WS2812, EXAMPLE_PIN_NUM_DATA4, GRB>(
|
||||
leds + (4 * NUM_LEDS_PER_STRIP), NUM_LEDS_PER_STRIP
|
||||
);
|
||||
FastLED.addLeds<WS2812, EXAMPLE_PIN_NUM_DATA5, GRB>(
|
||||
leds + (5 * NUM_LEDS_PER_STRIP), NUM_LEDS_PER_STRIP
|
||||
);
|
||||
FastLED.addLeds<WS2812, EXAMPLE_PIN_NUM_DATA6, GRB>(
|
||||
leds + (6 * NUM_LEDS_PER_STRIP), NUM_LEDS_PER_STRIP
|
||||
);
|
||||
FastLED.addLeds<WS2812, EXAMPLE_PIN_NUM_DATA7, GRB>(
|
||||
leds + (7 * NUM_LEDS_PER_STRIP), NUM_LEDS_PER_STRIP
|
||||
);
|
||||
FastLED.addLeds<WS2812, EXAMPLE_PIN_NUM_DATA8, GRB>(
|
||||
leds + (8 * NUM_LEDS_PER_STRIP), NUM_LEDS_PER_STRIP
|
||||
);
|
||||
FastLED.addLeds<WS2812, EXAMPLE_PIN_NUM_DATA9, GRB>(
|
||||
leds + (9 * NUM_LEDS_PER_STRIP), NUM_LEDS_PER_STRIP
|
||||
);
|
||||
FastLED.addLeds<WS2812, EXAMPLE_PIN_NUM_DATA10, GRB>(
|
||||
leds + (10 * NUM_LEDS_PER_STRIP), NUM_LEDS_PER_STRIP
|
||||
);
|
||||
FastLED.addLeds<WS2812, EXAMPLE_PIN_NUM_DATA11, GRB>(
|
||||
leds + (11 * NUM_LEDS_PER_STRIP), NUM_LEDS_PER_STRIP
|
||||
);
|
||||
FastLED.addLeds<WS2812, EXAMPLE_PIN_NUM_DATA12, GRB>(
|
||||
leds + (12 * NUM_LEDS_PER_STRIP), NUM_LEDS_PER_STRIP
|
||||
);
|
||||
FastLED.addLeds<WS2812, EXAMPLE_PIN_NUM_DATA13, GRB>(
|
||||
leds + (13 * NUM_LEDS_PER_STRIP), NUM_LEDS_PER_STRIP
|
||||
);
|
||||
FastLED.addLeds<WS2812, EXAMPLE_PIN_NUM_DATA14, GRB>(
|
||||
leds + (14 * NUM_LEDS_PER_STRIP), NUM_LEDS_PER_STRIP
|
||||
);
|
||||
FastLED.addLeds<WS2812, EXAMPLE_PIN_NUM_DATA15, GRB>(
|
||||
leds + (15 * NUM_LEDS_PER_STRIP), NUM_LEDS_PER_STRIP
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void setup() {
|
||||
// put your setup code here, to run once:
|
||||
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());
|
||||
log_d("Free heap: %d", ESP.getFreeHeap());
|
||||
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 seconds before startup");
|
||||
delay(6000); // The long reset time here is to make it easier to flash the device during the development process.
|
||||
|
||||
setup_i2s();
|
||||
FastLED.setBrightness(32);
|
||||
|
||||
}
|
||||
|
||||
void fill_rainbow(CRGB* all_leds) {
|
||||
static int s_offset = 0;
|
||||
for (int j = 0; j < NUMSTRIPS; j++) {
|
||||
for (int i = 0; i < NUM_LEDS_PER_STRIP; i++) {
|
||||
int idx = (i + s_offset) % NUM_LEDS_PER_STRIP + NUM_LEDS_PER_STRIP * j;
|
||||
all_leds[idx] = CHSV(i, 255, 255);
|
||||
}
|
||||
}
|
||||
s_offset++;
|
||||
}
|
||||
|
||||
void loop() {
|
||||
fill_rainbow(leds);
|
||||
FastLED.show();
|
||||
|
||||
}
|
||||
|
||||
#else // ESP32
|
||||
|
||||
// Non-ESP32 platform - provide minimal example for compilation testing
|
||||
#include "FastLED.h"
|
||||
|
||||
#define NUM_LEDS 16
|
||||
#define DATA_PIN 3
|
||||
|
||||
CRGB leds[NUM_LEDS];
|
||||
|
||||
void setup() {
|
||||
FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
fill_rainbow(leds, NUM_LEDS, 0, 7);
|
||||
FastLED.show();
|
||||
delay(50);
|
||||
}
|
||||
|
||||
#endif // ESP32
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
|
||||
#ifndef ESP32
|
||||
#define IS_ESP32_S3 0
|
||||
#else
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32S3
|
||||
#define IS_ESP32_S3 1
|
||||
#else
|
||||
#define IS_ESP32_S3 0
|
||||
#endif // CONFIG_IDF_TARGET_ESP32
|
||||
#endif // ESP32
|
||||
|
||||
#if IS_ESP32_S3
|
||||
#include "Esp32S3I2SDemo.h"
|
||||
#else
|
||||
#include "platforms/sketch_fake.hpp"
|
||||
#endif
|
||||
@@ -0,0 +1,74 @@
|
||||
// Simple test for the I2S on the ESP32dev board.
|
||||
// IMPORTANT:
|
||||
// This is using examples is built on esp-idf 4.x. This existed prior to Arduino Core 3.0.0.
|
||||
// To use this example, you MUST downgrade to Arduino Core < 3.0.0
|
||||
// or it won't work on Arduino.
|
||||
|
||||
#ifdef ESP32
|
||||
|
||||
|
||||
|
||||
#define FASTLED_ESP32_I2S
|
||||
#include <FastLED.h>
|
||||
|
||||
// How many leds in your strip?
|
||||
#define NUM_LEDS 1
|
||||
|
||||
// For led chips like WS2812, which have a data line, ground, and power, you just
|
||||
// need to define DATA_PIN. For led chipsets that are SPI based (four wires - data, clock,
|
||||
// ground, and power), like the LPD8806 define both DATA_PIN and CLOCK_PIN
|
||||
// Clock pin only needed for SPI based chipsets when not using hardware SPI
|
||||
#define DATA_PIN 3
|
||||
|
||||
// Define the array of leds
|
||||
CRGB leds[NUM_LEDS];
|
||||
|
||||
void setup() {
|
||||
// Uncomment/edit one of the following lines for your leds arrangement.
|
||||
// ## Clockless types ##
|
||||
FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS); // GRB ordering is assumed
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Turn the LED on, then pause
|
||||
leds[0] = CRGB::Red;
|
||||
FastLED.show();
|
||||
delay(500);
|
||||
// Now turn the LED off, then pause
|
||||
leds[0] = CRGB::Black;
|
||||
FastLED.show();
|
||||
delay(500);
|
||||
|
||||
// This is a no-op but tests that we have access to gCntBuffer, part of the
|
||||
// i2s api. You can delete this in your own sketch. It's only here for testing
|
||||
// purposes.
|
||||
if (false) {
|
||||
int value = gCntBuffer;
|
||||
value++;
|
||||
}
|
||||
}
|
||||
|
||||
#else // ESP32
|
||||
|
||||
// Non-ESP32 platform - provide minimal example for compilation testing
|
||||
#include <FastLED.h>
|
||||
|
||||
#define NUM_LEDS 1
|
||||
#define DATA_PIN 3
|
||||
|
||||
CRGB leds[NUM_LEDS];
|
||||
|
||||
void setup() {
|
||||
FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
leds[0] = CRGB::Red;
|
||||
FastLED.show();
|
||||
delay(500);
|
||||
leds[0] = CRGB::Black;
|
||||
FastLED.show();
|
||||
delay(500);
|
||||
}
|
||||
|
||||
#endif // ESP32
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
#include "fl/has_include.h"
|
||||
|
||||
|
||||
// Platform must be esp32.
|
||||
#if !defined(ESP32) || !FL_HAS_INCLUDE("sdkconfig.h")
|
||||
#define IS_ESP32_DEV 0
|
||||
#else
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#define IS_ESP32_DEV 1
|
||||
#else
|
||||
#define IS_ESP32_DEV 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if IS_ESP32_DEV
|
||||
#include "EspI2SDemo.h"
|
||||
#else
|
||||
#include "platforms/sketch_fake.hpp"
|
||||
#endif
|
||||
@@ -0,0 +1,14 @@
|
||||
#include "FastLED.h"
|
||||
|
||||
|
||||
#include "fl/sketch_macros.h"
|
||||
|
||||
#if !SKETCH_HAS_LOTS_OF_MEMORY
|
||||
// Platform does not have enough memory
|
||||
void setup() {}
|
||||
void loop() {}
|
||||
#else
|
||||
|
||||
#include "curr.h"
|
||||
|
||||
#endif
|
||||
811
.pio/libdeps/esp01_1m/FastLED/examples/FestivalStick/curr.h
Normal file
811
.pio/libdeps/esp01_1m/FastLED/examples/FestivalStick/curr.h
Normal file
@@ -0,0 +1,811 @@
|
||||
/*
|
||||
Festival Stick - Corkscrew LED Mapping Demo
|
||||
|
||||
This example demonstrates proper corkscrew LED mapping for a festival stick
|
||||
(19+ turns, 288 LEDs) using the new Corkscrew ScreenMap functionality.
|
||||
|
||||
Key Features:
|
||||
- Uses Corkscrew.toScreenMap() for accurate web interface visualization
|
||||
- Draws patterns into a rectangular grid (frameBuffer)
|
||||
- Maps the rectangular grid to the corkscrew LED positions using readFrom()
|
||||
- Supports both noise patterns and manual LED positioning
|
||||
- Proper color boost and brightness controls
|
||||
|
||||
Workflow:
|
||||
1. Draw patterns into frameBuffer (rectangular grid for easy 2D drawing)
|
||||
2. Use corkscrew.readFrom(frameBuffer) to map grid to corkscrew LED positions
|
||||
3. Display the corkscrew buffer directly via FastLED
|
||||
4. Web interface shows actual corkscrew spiral shape via ScreenMap
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "FastLED.h"
|
||||
#include "fl/compiler_control.h"
|
||||
|
||||
|
||||
#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 "fl/array.h"
|
||||
#include "fx/2d/wave.h"
|
||||
#include "fx/2d/blend.h"
|
||||
#include "fx/fx_engine.h"
|
||||
#include "fx/2d/animartrix.hpp"
|
||||
|
||||
// #include "vec3.h"
|
||||
|
||||
using namespace fl;
|
||||
|
||||
|
||||
|
||||
#ifndef PIN_DATA
|
||||
#define PIN_DATA 1 // Universally available pin
|
||||
#endif
|
||||
|
||||
#ifndef PIN_CLOCK
|
||||
#define PIN_CLOCK 2 // Universally available pin
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef TEST
|
||||
#define NUM_LEDS 4
|
||||
#define CORKSCREW_TURNS 2 // Default to 19 turns
|
||||
#else
|
||||
#define NUM_LEDS 288
|
||||
#define CORKSCREW_TURNS 19.25 // Default to 19 turns
|
||||
#endif
|
||||
|
||||
// #define CM_BETWEEN_LEDS 1.0 // 1cm between LEDs
|
||||
// #define CM_LED_DIAMETER 0.5 // 0.5cm LED diameter
|
||||
|
||||
UITitle festivalStickTitle("Festival Stick - Advanced Version");
|
||||
UIDescription festivalStickDescription(
|
||||
"# Festival Stick Demo\n\n"
|
||||
"This example demonstrates **proper corkscrew LED mapping** for a festival stick using FastLED's advanced mapping capabilities.\n\n"
|
||||
"## Key Features\n"
|
||||
"- **19+ turns** with 288 LEDs total\n"
|
||||
"- Uses `Corkscrew.toScreenMap()` for accurate web interface visualization\n"
|
||||
"- Multiple render modes: **Noise**, **Position**, **Fire**, **Wave**, and **Animartrix** effects\n"
|
||||
"- Real-time cylindrical surface mapping\n"
|
||||
"- **Wave mode**: Cylindrical 2D wave simulation with ripple effects and configurable blur\n"
|
||||
"- **Animartrix mode**: Advanced 2D animation effects with polar coordinate patterns\n\n"
|
||||
"## How It Works\n"
|
||||
"1. Draws patterns into a rectangular grid (`frameBuffer`)\n"
|
||||
"2. Maps the grid to corkscrew LED positions using `readFrom()`\n"
|
||||
"3. Web interface shows the actual spiral shape via ScreenMap\n\n"
|
||||
"*Select different render modes and adjust parameters to see various effects!*");
|
||||
|
||||
// UIHelp festivalStickHelp("Festival Stick - Advanced Guide");
|
||||
|
||||
// UIHelp corkscrewMappingHelp("Understanding Corkscrew Mapping");
|
||||
|
||||
// UIHelp uiControlsHelp("UI Controls Guide");
|
||||
|
||||
|
||||
|
||||
UISlider speed("Speed", 0.1f, 0.01f, 1.0f, 0.01f);
|
||||
UISlider positionCoarse("Position Coarse (10x)", 0.0f, 0.0f, 1.0f, 0.01f);
|
||||
UISlider positionFine("Position Fine (1x)", 0.0f, 0.0f, 0.1f, 0.001f);
|
||||
UISlider positionExtraFine("Position Extra Fine (0.1x)", 0.0f, 0.0f, 0.01f, 0.0001f);
|
||||
UISlider brightness("Brightness", 255, 0, 255, 1);
|
||||
|
||||
UICheckbox autoAdvance("Auto Advance", true);
|
||||
UICheckbox allWhite("All White", false);
|
||||
UICheckbox splatRendering("Splat Rendering", true);
|
||||
|
||||
// Noise controls (grouped under noiseGroup)
|
||||
|
||||
UISlider noiseScale("Noise Scale", 100, 10, 200, 5);
|
||||
UISlider noiseSpeed("Noise Speed", 4, 1, 100, 1);
|
||||
|
||||
// UIDropdown examples - noise-related color palette
|
||||
string paletteOptions[] = {"Party", "Heat", "Ocean", "Forest", "Rainbow"};
|
||||
string renderModeOptions[] = { "Wave", "Animartrix", "Noise", "Position", "Fire" };
|
||||
|
||||
|
||||
UIDropdown paletteDropdown("Color Palette", paletteOptions);
|
||||
UIDropdown renderModeDropdown("Render Mode", renderModeOptions);
|
||||
|
||||
|
||||
// fl::array<fl::pair<int, fl::string>> easeInfo = {
|
||||
// pair(EASE_IN_QUAD, "EASE_IN_QUAD"),
|
||||
// pair(EASE_OUT_QUAD, "EASE_OUT_QUAD"),
|
||||
// pair(EASE_IN_OUT_QUAD, "EASE_IN_OUT_QUAD"),
|
||||
// pair(EASE_IN_CUBIC, "EASE_IN_CUBIC"),
|
||||
// pair(EASE_OUT_CUBIC, "EASE_OUT_CUBIC"),
|
||||
// pair(EASE_IN_OUT_CUBIC, "EASE_IN_OUT_CUBIC"),
|
||||
// pair(EASE_IN_SINE, "EASE_IN_SINE"),
|
||||
// pair(EASE_OUT_SINE, "EASE_OUT_SINE"),
|
||||
// pair(EASE_IN_OUT_SINE, "EASE_IN_OUT_SINE")
|
||||
// };
|
||||
|
||||
|
||||
fl::vector<fl::string> easeInfo = {
|
||||
"EASE_NONE",
|
||||
"EASE_IN_QUAD",
|
||||
"EASE_OUT_QUAD",
|
||||
"EASE_IN_OUT_QUAD",
|
||||
"EASE_IN_CUBIC",
|
||||
"EASE_OUT_CUBIC",
|
||||
"EASE_IN_OUT_CUBIC",
|
||||
"EASE_IN_SINE",
|
||||
"EASE_OUT_SINE",
|
||||
"EASE_IN_OUT_SINE"
|
||||
};
|
||||
|
||||
EaseType getEaseType(fl::string value) {
|
||||
if (value == "EASE_NONE") {
|
||||
return EASE_NONE;
|
||||
} else if (value == "EASE_IN_QUAD") {
|
||||
return EASE_IN_QUAD;
|
||||
} else if (value == "EASE_OUT_QUAD") {
|
||||
return EASE_OUT_QUAD;
|
||||
} else if (value == "EASE_IN_OUT_QUAD") {
|
||||
return EASE_IN_OUT_QUAD;
|
||||
} else if (value == "EASE_IN_CUBIC") {
|
||||
return EASE_IN_CUBIC;
|
||||
} else if (value == "EASE_OUT_CUBIC") {
|
||||
return EASE_OUT_CUBIC;
|
||||
} else if (value == "EASE_IN_OUT_CUBIC") {
|
||||
return EASE_IN_OUT_CUBIC;
|
||||
} else if (value == "EASE_IN_SINE") {
|
||||
return EASE_IN_SINE;
|
||||
} else if (value == "EASE_OUT_SINE") {
|
||||
return EASE_OUT_SINE;
|
||||
} else if (value == "EASE_IN_OUT_SINE") {
|
||||
return EASE_IN_OUT_SINE;
|
||||
} else {
|
||||
return EASE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
// Color boost controls
|
||||
UIDropdown saturationFunction("Saturation Function", easeInfo);
|
||||
UIDropdown luminanceFunction("Luminance Function", easeInfo);
|
||||
|
||||
// Fire-related UI controls (added for cylindrical fire effect)
|
||||
UISlider fireScaleXY("Fire Scale", 8, 1, 100, 1);
|
||||
UISlider fireSpeedY("Fire SpeedY", 1.3, 1, 6, .1);
|
||||
UISlider fireScaleX("Fire ScaleX", .3, 0.1, 3, .01);
|
||||
UISlider fireInvSpeedZ("Fire Inverse SpeedZ", 20, 1, 100, 1);
|
||||
UINumberField firePalette("Fire Palette", 0, 0, 2);
|
||||
|
||||
// Wave-related UI controls (cylindrical wave effects)
|
||||
UISlider waveSpeed("Wave Speed", 0.03f, 0.0f, 1.0f, 0.01f);
|
||||
UISlider waveDampening("Wave Dampening", 9.1f, 0.0f, 20.0f, 0.1f);
|
||||
UICheckbox waveHalfDuplex("Wave Half Duplex", true);
|
||||
UICheckbox waveAutoTrigger("Wave Auto Trigger", true);
|
||||
UISlider waveTriggerSpeed("Wave Trigger Speed", 0.5f, 0.0f, 1.0f, 0.01f);
|
||||
UIButton waveTriggerButton("Trigger Wave");
|
||||
UINumberField wavePalette("Wave Palette", 0, 0, 2);
|
||||
|
||||
// Wave blur controls (added for smoother wave effects)
|
||||
UISlider waveBlurAmount("Wave Blur Amount", 50, 0, 172, 1);
|
||||
UISlider waveBlurPasses("Wave Blur Passes", 1, 1, 10, 1);
|
||||
|
||||
// Fire color palettes (from FireCylinder)
|
||||
DEFINE_GRADIENT_PALETTE(firepal){
|
||||
0, 0, 0, 0,
|
||||
32, 255, 0, 0,
|
||||
190, 255, 255, 0,
|
||||
255, 255, 255, 255
|
||||
};
|
||||
|
||||
DEFINE_GRADIENT_PALETTE(electricGreenFirePal){
|
||||
0, 0, 0, 0,
|
||||
32, 0, 70, 0,
|
||||
190, 57, 255, 20,
|
||||
255, 255, 255, 255
|
||||
};
|
||||
|
||||
DEFINE_GRADIENT_PALETTE(electricBlueFirePal){
|
||||
0, 0, 0, 0,
|
||||
32, 0, 0, 70,
|
||||
128, 20, 57, 255,
|
||||
255, 255, 255, 255
|
||||
};
|
||||
|
||||
// Wave color palettes (for cylindrical wave effects)
|
||||
DEFINE_GRADIENT_PALETTE(waveBluepal){
|
||||
0, 0, 0, 0, // Black (no wave)
|
||||
32, 0, 0, 70, // Dark blue (low wave)
|
||||
128, 20, 57, 255, // Electric blue (medium wave)
|
||||
255, 255, 255, 255 // White (high wave)
|
||||
};
|
||||
|
||||
DEFINE_GRADIENT_PALETTE(waveGreenpal){
|
||||
0, 0, 0, 0, // Black (no wave)
|
||||
8, 128, 64, 64, // Green with red tint (very low wave)
|
||||
16, 255, 222, 222, // Pinkish red (low wave)
|
||||
64, 255, 255, 255, // White (medium wave)
|
||||
255, 255, 255, 255 // White (high wave)
|
||||
};
|
||||
|
||||
DEFINE_GRADIENT_PALETTE(waveRainbowpal){
|
||||
0, 255, 0, 0, // Red (no wave)
|
||||
64, 255, 127, 0, // Orange (low wave)
|
||||
128, 255, 255, 0, // Yellow (medium wave)
|
||||
192, 0, 255, 0, // Green (high wave)
|
||||
255, 0, 0, 255 // Blue (maximum wave)
|
||||
};
|
||||
|
||||
// Create UIGroup for noise controls using variadic constructor
|
||||
// This automatically assigns all specified controls to the "Noise Controls" group
|
||||
UIGroup noiseGroup("Noise Controls", noiseScale, noiseSpeed, paletteDropdown);
|
||||
UIGroup fireGroup("Fire Controls", fireScaleXY, fireSpeedY, fireScaleX, fireInvSpeedZ, firePalette);
|
||||
UIGroup waveGroup("Wave Controls", waveSpeed, waveDampening, waveHalfDuplex, waveAutoTrigger, waveTriggerSpeed, waveTriggerButton, wavePalette, waveBlurAmount, waveBlurPasses);
|
||||
UIGroup renderGroup("Render Options", renderModeDropdown, splatRendering, allWhite, brightness);
|
||||
UIGroup colorBoostGroup("Color Boost", saturationFunction, luminanceFunction);
|
||||
UIGroup pointGraphicsGroup("Point Graphics Mode", speed, positionCoarse, positionFine, positionExtraFine, autoAdvance);
|
||||
|
||||
// Animartrix-related UI controls
|
||||
UINumberField animartrixIndex("Animartrix Animation", 5, 0, NUM_ANIMATIONS - 1);
|
||||
UINumberField animartrixColorOrder("Animartrix Color Order", 0, 0, 5);
|
||||
UISlider animartrixTimeSpeed("Animartrix Time Speed", 1, -10, 10, .1);
|
||||
|
||||
UIGroup animartrixGroup("Animartrix Controls", animartrixIndex, animartrixTimeSpeed, animartrixColorOrder);
|
||||
|
||||
// Color palette for noise
|
||||
CRGBPalette16 noisePalette = PartyColors_p;
|
||||
uint8_t colorLoop = 1;
|
||||
|
||||
// Option 1: Runtime Corkscrew (flexible, configurable at runtime)
|
||||
Corkscrew corkscrew(CORKSCREW_TURNS, NUM_LEDS);
|
||||
|
||||
// Simple position tracking - one variable for both modes
|
||||
static float currentPosition = 0.0f;
|
||||
static uint32_t lastUpdateTime = 0;
|
||||
|
||||
// Wave effect globals
|
||||
static uint32_t nextWaveTrigger = 0;
|
||||
|
||||
|
||||
|
||||
|
||||
// Option 2: Constexpr dimensions for compile-time array sizing
|
||||
constexpr uint16_t CORKSCREW_WIDTH =
|
||||
calculateCorkscrewWidth(CORKSCREW_TURNS, NUM_LEDS);
|
||||
constexpr uint16_t CORKSCREW_HEIGHT =
|
||||
calculateCorkscrewHeight(CORKSCREW_TURNS, NUM_LEDS);
|
||||
|
||||
// Now you can use these for array initialization:
|
||||
// CRGB frameBuffer[CORKSCREW_WIDTH * CORKSCREW_HEIGHT]; // Compile-time sized
|
||||
// array
|
||||
|
||||
// Create a corkscrew with:
|
||||
// - 30cm total length (300mm)
|
||||
// - 5cm width (50mm)
|
||||
// - 2mm LED inner diameter
|
||||
// - 24 LEDs per turn
|
||||
// ScreenMap screenMap = makeCorkScrew(NUM_LEDS,
|
||||
// 300.0f, 50.0f, 2.0f, 24.0f);
|
||||
|
||||
// vector<vec3f> mapCorkScrew = makeCorkScrew(args);
|
||||
ScreenMap screenMap;
|
||||
fl::shared_ptr<Grid<CRGB>> frameBufferPtr;
|
||||
|
||||
// Wave effect objects - declared here but initialized in setup()
|
||||
WaveFxPtr waveFx;
|
||||
Blend2dPtr waveBlend;
|
||||
|
||||
// Animartrix effect objects - declared here but initialized in setup()
|
||||
fl::unique_ptr<Animartrix> animartrix;
|
||||
fl::unique_ptr<FxEngine> fxEngine;
|
||||
WaveCrgbGradientMapPtr crgMap = fl::make_shared<WaveCrgbGradientMap>();
|
||||
|
||||
void setup() {
|
||||
// Use constexpr dimensions (computed at compile time)
|
||||
constexpr int width = CORKSCREW_WIDTH; // = 16
|
||||
constexpr int height = CORKSCREW_HEIGHT; // = 18
|
||||
|
||||
|
||||
// Noise controls are now automatically grouped by the UIGroup constructor
|
||||
// The noiseGroup variadic constructor automatically called setGroup() on all controls
|
||||
|
||||
|
||||
// Or use runtime corkscrew for dynamic sizing
|
||||
// int width = corkscrew.cylinder_width();
|
||||
// int height = corkscrew.cylinder_height();
|
||||
|
||||
XYMap xyMap = XYMap::constructRectangularGrid(width, height, 0);
|
||||
|
||||
// Use the corkscrew's internal buffer for the LED strip
|
||||
CLEDController *controller =
|
||||
&FastLED.addLeds<APA102HD, PIN_DATA, PIN_CLOCK, BGR>(corkscrew.rawData(), NUM_LEDS);
|
||||
|
||||
// CLEDController *controller =
|
||||
// &FastLED.addLeds<WS2812, 3, BGR>(stripLeds, NUM_LEDS);
|
||||
|
||||
// NEW: Create ScreenMap directly from Corkscrew using toScreenMap()
|
||||
// This maps each LED index to its exact position on the corkscrew spiral
|
||||
// instead of using a rectangular grid mapping
|
||||
ScreenMap corkscrewScreenMap = corkscrew.toScreenMap(0.2f);
|
||||
|
||||
// OLD WAY (rectangular grid - not accurate for corkscrew visualization):
|
||||
// ScreenMap screenMap = xyMap.toScreenMap();
|
||||
// screenMap.setDiameter(.2f);
|
||||
|
||||
// Set the corkscrew screen map for the controller
|
||||
// This allows the web interface to display the actual corkscrew spiral shape
|
||||
controller->setScreenMap(corkscrewScreenMap);
|
||||
|
||||
// Initialize wave effects for cylindrical surface
|
||||
XYMap xyRect(width, height, false); // Rectangular grid for wave simulation
|
||||
WaveFx::Args waveArgs;
|
||||
waveArgs.factor = SuperSample::SUPER_SAMPLE_2X; // 2x supersampling for smoother waves
|
||||
waveArgs.half_duplex = true; // Only positive waves
|
||||
waveArgs.auto_updates = true; // Auto-update simulation
|
||||
waveArgs.speed = 0.16f; // Wave propagation speed
|
||||
waveArgs.dampening = 6.0f; // Wave energy loss
|
||||
waveArgs.x_cyclical = true; // Enable cylindrical wrapping!
|
||||
waveArgs.crgbMap = fl::make_shared<WaveCrgbGradientMap>(waveBluepal); // Default color palette
|
||||
|
||||
// Create wave effect with cylindrical mapping
|
||||
waveFx = fl::make_shared<WaveFx>(xyRect, waveArgs);
|
||||
|
||||
// Create blender for wave effects (allows multiple wave layers in future)
|
||||
waveBlend = fl::make_shared<Blend2d>(xyRect);
|
||||
waveBlend->add(waveFx);
|
||||
|
||||
// Initialize Animartrix effect
|
||||
XYMap animartrixXyMap = XYMap::constructRectangularGrid(width, height, 0);
|
||||
animartrix.reset(new Animartrix(animartrixXyMap, POLAR_WAVES));
|
||||
fxEngine.reset(new FxEngine(width * height));
|
||||
fxEngine->addFx(*animartrix);
|
||||
|
||||
// Demonstrate UIGroup functionality for noise controls
|
||||
FL_WARN("Noise UI Group initialized: " << noiseGroup.name());
|
||||
FL_WARN(" This group contains noise pattern controls:");
|
||||
FL_WARN(" - Use Noise Pattern toggle");
|
||||
FL_WARN(" - Noise Scale and Speed sliders");
|
||||
FL_WARN(" - Color Palette selection for noise");
|
||||
FL_WARN(" UIGroup automatically applied group membership via variadic constructor");
|
||||
|
||||
// Set initial dropdown selections
|
||||
paletteDropdown.setSelectedIndex(0); // Party
|
||||
renderModeDropdown.setSelectedIndex(0); // Fire (new default)
|
||||
|
||||
// Add onChange callbacks for dropdowns
|
||||
paletteDropdown.onChanged([](UIDropdown &dropdown) {
|
||||
string selectedPalette = dropdown.value();
|
||||
FL_WARN("Noise palette changed to: " << selectedPalette);
|
||||
if (selectedPalette == "Party") {
|
||||
noisePalette = PartyColors_p;
|
||||
} else if (selectedPalette == "Heat") {
|
||||
noisePalette = HeatColors_p;
|
||||
} else if (selectedPalette == "Ocean") {
|
||||
noisePalette = OceanColors_p;
|
||||
} else if (selectedPalette == "Forest") {
|
||||
noisePalette = ForestColors_p;
|
||||
} else if (selectedPalette == "Rainbow") {
|
||||
noisePalette = RainbowColors_p;
|
||||
}
|
||||
});
|
||||
|
||||
renderModeDropdown.onChanged([](UIDropdown &dropdown) {
|
||||
string mode = dropdown.value();
|
||||
// Simple example of using getOption()
|
||||
for(size_t i = 0; i < dropdown.getOptionCount(); i++) {
|
||||
if(dropdown.getOption(i) == mode) {
|
||||
FL_WARN("Render mode changed to: " << mode);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Add onChange callback for animartrix color order
|
||||
animartrixColorOrder.onChanged([](int value) {
|
||||
EOrder order = RGB;
|
||||
switch(value) {
|
||||
case 0: order = RGB; break;
|
||||
case 1: order = RBG; break;
|
||||
case 2: order = GRB; break;
|
||||
case 3: order = GBR; break;
|
||||
case 4: order = BRG; break;
|
||||
case 5: order = BGR; break;
|
||||
}
|
||||
if (animartrix.get()) {
|
||||
animartrix->setColorOrder(order);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
waveFx->setCrgbMap(crgMap);
|
||||
|
||||
frameBufferPtr = corkscrew.getOrCreateInputSurface();
|
||||
}
|
||||
|
||||
|
||||
FL_OPTIMIZATION_LEVEL_O0_BEGIN // Works around a compile bug in clang 19
|
||||
float get_position(uint32_t now) {
|
||||
if (autoAdvance.value()) {
|
||||
// Check if auto-advance was just enabled
|
||||
// Auto-advance mode: increment smoothly from current position
|
||||
float elapsedSeconds = float(now - lastUpdateTime) / 1000.0f;
|
||||
float increment = elapsedSeconds * speed.value() *
|
||||
0.3f; // Make it 1/20th the original speed
|
||||
currentPosition = fmodf(currentPosition + increment, 1.0f);
|
||||
lastUpdateTime = now;
|
||||
return currentPosition;
|
||||
} else {
|
||||
// Manual mode: use the dual slider control
|
||||
float combinedPosition = positionCoarse.value() + positionFine.value() + positionExtraFine.value();
|
||||
// Clamp to ensure we don't exceed 1.0
|
||||
if (combinedPosition > 1.0f)
|
||||
combinedPosition = 1.0f;
|
||||
return combinedPosition;
|
||||
}
|
||||
}
|
||||
FL_OPTIMIZATION_LEVEL_O0_END
|
||||
|
||||
void fillFrameBufferNoise() {
|
||||
// Get current UI values
|
||||
uint8_t noise_scale = noiseScale.value();
|
||||
uint8_t noise_speed = noiseSpeed.value();
|
||||
|
||||
// Derive noise coordinates from current time instead of forward iteration
|
||||
uint32_t now = millis();
|
||||
uint16_t noise_z = now * noise_speed / 10; // Primary time dimension
|
||||
uint16_t noise_x = now * noise_speed / 80; // Slow drift in x
|
||||
uint16_t noise_y = now * noise_speed / 160; // Even slower drift in y (opposite direction)
|
||||
|
||||
int width = frameBufferPtr->width();
|
||||
int height = frameBufferPtr->height();
|
||||
|
||||
// Data smoothing for low speeds (from NoisePlusPalette example)
|
||||
uint8_t dataSmoothing = 0;
|
||||
if(noise_speed < 50) {
|
||||
dataSmoothing = 200 - (noise_speed * 4);
|
||||
}
|
||||
|
||||
// Generate noise for each pixel in the frame buffer using cylindrical mapping
|
||||
for(int x = 0; x < width; x++) {
|
||||
for(int y = 0; y < height; y++) {
|
||||
// Convert rectangular coordinates to cylindrical coordinates
|
||||
// Map x to angle (0 to 2*PI), y remains as height
|
||||
float angle = (float(x) / float(width)) * 2.0f * PI;
|
||||
|
||||
// Convert cylindrical coordinates to cartesian for noise sampling
|
||||
// Use the noise_scale to control the cylinder size in noise space
|
||||
float cylinder_radius = noise_scale; // Use the existing noise_scale parameter
|
||||
|
||||
// Calculate cartesian coordinates on the cylinder surface
|
||||
float noise_x_cyl = cos(angle) * cylinder_radius;
|
||||
float noise_y_cyl = sin(angle) * cylinder_radius;
|
||||
float noise_z_height = float(y) * noise_scale; // Height component
|
||||
|
||||
// Apply time-based offsets
|
||||
int xoffset = int(noise_x_cyl) + noise_x;
|
||||
int yoffset = int(noise_y_cyl) + noise_y;
|
||||
int zoffset = int(noise_z_height) + noise_z;
|
||||
|
||||
// Generate 8-bit noise value using 3D Perlin noise with cylindrical coordinates
|
||||
uint8_t data = inoise8(xoffset, yoffset, zoffset);
|
||||
|
||||
// Expand the range from ~16-238 to 0-255 (from NoisePlusPalette)
|
||||
data = qsub8(data, 16);
|
||||
data = qadd8(data, scale8(data, 39));
|
||||
|
||||
// Apply data smoothing if enabled
|
||||
if(dataSmoothing) {
|
||||
CRGB oldColor = frameBufferPtr->at(x, y);
|
||||
uint8_t olddata = (oldColor.r + oldColor.g + oldColor.b) / 3; // Simple brightness extraction
|
||||
uint8_t newdata = scale8(olddata, dataSmoothing) + scale8(data, 256 - dataSmoothing);
|
||||
data = newdata;
|
||||
}
|
||||
|
||||
// Map noise to color using palette (adapted from NoisePlusPalette)
|
||||
uint8_t index = data;
|
||||
uint8_t bri = data;
|
||||
|
||||
// Add color cycling if enabled - also derive from time
|
||||
uint8_t ihue = 0;
|
||||
if(colorLoop) {
|
||||
ihue = (now / 100) % 256; // Derive hue from time instead of incrementing
|
||||
index += ihue;
|
||||
}
|
||||
|
||||
// Enhance brightness (from NoisePlusPalette example)
|
||||
// if(bri > 127) {
|
||||
// //bri = 255;
|
||||
// } else {
|
||||
// //bri = dim8_raw(bri * 2);
|
||||
// }
|
||||
|
||||
// Get color from palette and set pixel
|
||||
CRGB color = ColorFromPalette(noisePalette, index, bri);
|
||||
|
||||
// Apply color boost using ease functions
|
||||
EaseType sat_ease = getEaseType(saturationFunction.value());
|
||||
EaseType lum_ease = getEaseType(luminanceFunction.value());
|
||||
color = color.colorBoost(sat_ease, lum_ease);
|
||||
|
||||
frameBufferPtr->at(x, y) = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void drawNoise(uint32_t now) {
|
||||
FL_UNUSED(now);
|
||||
fillFrameBufferNoise();
|
||||
}
|
||||
|
||||
void draw(float pos) {
|
||||
if (splatRendering) {
|
||||
Tile2x2_u8_wrap pos_tile = corkscrew.at_wrap(pos);
|
||||
//FL_WARN("pos_tile: " << pos_tile);
|
||||
CRGB color = CRGB::Blue;
|
||||
// Apply color boost using ease functions
|
||||
EaseType sat_ease = getEaseType(saturationFunction.value());
|
||||
EaseType lum_ease = getEaseType(luminanceFunction.value());
|
||||
color = color.colorBoost(sat_ease, lum_ease);
|
||||
// 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) {
|
||||
Tile2x2_u8_wrap::Entry data = pos_tile.at(dx, dy);
|
||||
vec2<u16> 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
|
||||
frameBufferPtr->at(wrapped_pos.x, wrapped_pos.y) = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// None splat rendering, looks aweful.
|
||||
vec2f pos_vec2f = corkscrew.at_no_wrap(pos);
|
||||
vec2<u16> pos_i16 = vec2<u16>(pos_vec2f.x, pos_vec2f.y);
|
||||
|
||||
CRGB color = CRGB::Blue;
|
||||
// Apply color boost using ease functions
|
||||
EaseType sat_ease = getEaseType(saturationFunction.value());
|
||||
EaseType lum_ease = getEaseType(luminanceFunction.value());
|
||||
color = color.colorBoost(sat_ease, lum_ease);
|
||||
|
||||
// Now map the cork screw position to the cylindrical buffer that we
|
||||
// will draw.
|
||||
frameBufferPtr->at(pos_i16.x, pos_i16.y) = color; // Draw a blue pixel at (w, h)
|
||||
}
|
||||
}
|
||||
|
||||
CRGBPalette16 getFirePalette() {
|
||||
int paletteIndex = (int)firePalette.value();
|
||||
switch (paletteIndex) {
|
||||
case 0:
|
||||
return firepal;
|
||||
case 1:
|
||||
return electricGreenFirePal;
|
||||
case 2:
|
||||
return electricBlueFirePal;
|
||||
default:
|
||||
return firepal;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t getFirePaletteIndex(uint32_t millis32, int width, int max_width, int height, int max_height,
|
||||
uint32_t y_speed) {
|
||||
uint16_t scale = fireScaleXY.as<uint16_t>();
|
||||
|
||||
float xf = (float)width / (float)max_width;
|
||||
uint8_t x = (uint8_t)(xf * 255);
|
||||
|
||||
uint32_t cosx = cos8(x);
|
||||
uint32_t sinx = sin8(x);
|
||||
|
||||
float trig_scale = scale * fireScaleX.value();
|
||||
cosx *= trig_scale;
|
||||
sinx *= trig_scale;
|
||||
|
||||
uint32_t y = height * scale + y_speed;
|
||||
|
||||
uint16_t z = millis32 / fireInvSpeedZ.as<uint16_t>();
|
||||
|
||||
uint16_t noise16 = inoise16(cosx << 8, sinx << 8, y << 8, z << 8);
|
||||
|
||||
uint8_t noise_val = noise16 >> 8;
|
||||
|
||||
int8_t subtraction_factor = abs8(height - (max_height - 1)) * 255 /
|
||||
(max_height - 1);
|
||||
|
||||
return qsub8(noise_val, subtraction_factor);
|
||||
}
|
||||
|
||||
void fillFrameBufferFire(uint32_t now) {
|
||||
CRGBPalette16 myPal = getFirePalette();
|
||||
|
||||
// Calculate the current y-offset for animation (makes the fire move)
|
||||
uint32_t y_speed = now * fireSpeedY.value();
|
||||
|
||||
int width = frameBufferPtr->width();
|
||||
int height = frameBufferPtr->height();
|
||||
|
||||
// Loop through every pixel in our cylindrical matrix
|
||||
for (int w = 0; w < width; w++) {
|
||||
for (int h = 0; h < height; h++) {
|
||||
// Calculate which color to use from our palette for this pixel
|
||||
uint8_t palette_index =
|
||||
getFirePaletteIndex(now, w, width, h, height, y_speed);
|
||||
|
||||
// Get the actual RGB color from the palette
|
||||
CRGB color = ColorFromPalette(myPal, palette_index, 255);
|
||||
|
||||
// Apply color boost using ease functions
|
||||
EaseType sat_ease = getEaseType(saturationFunction.value());
|
||||
EaseType lum_ease = getEaseType(luminanceFunction.value());
|
||||
color = color.colorBoost(sat_ease, lum_ease);
|
||||
|
||||
// Set the pixel in the frame buffer
|
||||
// Flip coordinates to make fire rise from bottom
|
||||
frameBufferPtr->at((width - 1) - w, (height - 1) - h) = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void drawFire(uint32_t now) {
|
||||
fillFrameBufferFire(now);
|
||||
}
|
||||
|
||||
// Wave effect helper functions
|
||||
CRGBPalette16 getWavePalette() {
|
||||
int paletteIndex = (int)wavePalette.value();
|
||||
switch (paletteIndex) {
|
||||
case 0:
|
||||
return waveBluepal; // Electric blue waves
|
||||
case 1:
|
||||
return waveGreenpal; // Green/red waves
|
||||
case 2:
|
||||
return waveRainbowpal; // Rainbow waves
|
||||
default:
|
||||
return waveBluepal; // Default to blue
|
||||
}
|
||||
}
|
||||
|
||||
void triggerWaveRipple() {
|
||||
// Create a ripple at a random position within the central area
|
||||
float perc = 0.15f; // 15% margin from edges
|
||||
int width = corkscrew.cylinderWidth();
|
||||
int height = corkscrew.cylinderHeight();
|
||||
|
||||
int min_x = perc * width;
|
||||
int max_x = (1 - perc) * width;
|
||||
int min_y = perc * height;
|
||||
int max_y = (1 - perc) * height;
|
||||
|
||||
int x = random8(min_x, max_x);
|
||||
int y = random8(min_y, max_y);
|
||||
|
||||
// Trigger a 2x2 wave ripple for more punch (compensates for blur reduction)
|
||||
float ripple_strength = 1.5f; // Higher value for more impact
|
||||
waveFx->setf(x, y, ripple_strength);
|
||||
waveFx->setf(x + 1, y, ripple_strength);
|
||||
waveFx->setf(x, y + 1, ripple_strength);
|
||||
waveFx->setf(x + 1, y + 1, ripple_strength);
|
||||
|
||||
FL_WARN("Wave ripple triggered at (" << x << ", " << y << ") with 2x2 pattern");
|
||||
}
|
||||
|
||||
void processWaveAutoTrigger(uint32_t now) {
|
||||
// Handle automatic wave triggering
|
||||
if (waveAutoTrigger.value()) {
|
||||
if (now >= nextWaveTrigger) {
|
||||
triggerWaveRipple();
|
||||
|
||||
// Calculate next trigger time based on speed
|
||||
float speed = 1.0f - waveTriggerSpeed.value();
|
||||
uint32_t min_interval = (uint32_t)(500 * speed); // Minimum 500ms * speed
|
||||
uint32_t max_interval = (uint32_t)(3000 * speed); // Maximum 3000ms * speed
|
||||
|
||||
// Ensure valid range
|
||||
uint32_t min = MIN(min_interval, max_interval);
|
||||
uint32_t max = MAX(min_interval, max_interval);
|
||||
if (min >= max) max = min + 1;
|
||||
|
||||
nextWaveTrigger = now + random16(min, max);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void drawWave(uint32_t now) {
|
||||
// Update wave parameters from UI
|
||||
waveFx->setSpeed(waveSpeed.value());
|
||||
waveFx->setDampening(waveDampening.value());
|
||||
waveFx->setHalfDuplex(waveHalfDuplex.value());
|
||||
waveFx->setXCylindrical(true); // Always keep cylindrical for corkscrew
|
||||
|
||||
// Update wave color palette
|
||||
CRGBPalette16 currentPalette = getWavePalette();
|
||||
crgMap->setGradient(currentPalette);
|
||||
|
||||
|
||||
|
||||
// Apply blur settings to the wave blend (for smoother wave effects)
|
||||
waveBlend->setGlobalBlurAmount(waveBlurAmount.value());
|
||||
waveBlend->setGlobalBlurPasses(waveBlurPasses.value());
|
||||
|
||||
// Check if manual trigger button was pressed
|
||||
if (waveTriggerButton.value()) {
|
||||
triggerWaveRipple();
|
||||
}
|
||||
|
||||
// Handle auto-triggering
|
||||
processWaveAutoTrigger(now);
|
||||
|
||||
// Draw the wave effect directly to the frame buffer
|
||||
// Create a DrawContext for the wave renderer
|
||||
Fx::DrawContext waveContext(now, frameBufferPtr->data());
|
||||
waveBlend->draw(waveContext);
|
||||
}
|
||||
|
||||
void drawAnimartrix(uint32_t now) {
|
||||
// Update animartrix parameters from UI
|
||||
fxEngine->setSpeed(animartrixTimeSpeed.value());
|
||||
|
||||
// Handle animation index changes
|
||||
static int lastAnimartrixIndex = -1;
|
||||
if (animartrixIndex.value() != lastAnimartrixIndex) {
|
||||
lastAnimartrixIndex = animartrixIndex.value();
|
||||
animartrix->fxSet(animartrixIndex.value());
|
||||
}
|
||||
|
||||
// Draw the animartrix effect directly to the frame buffer
|
||||
CRGB* dst = corkscrew.rawData();
|
||||
fxEngine->draw(now, dst);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
delay(4);
|
||||
uint32_t now = millis();
|
||||
frameBufferPtr->clear();
|
||||
|
||||
if (allWhite) {
|
||||
CRGB whiteColor = CRGB(8, 8, 8);
|
||||
for (u32 x = 0; x < frameBufferPtr->width(); x++) {
|
||||
for (u32 y = 0; y < frameBufferPtr->height(); y++) {
|
||||
frameBufferPtr->at(x, y) = whiteColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Update the corkscrew mapping with auto-advance or manual position control
|
||||
float combinedPosition = get_position(now);
|
||||
float pos = combinedPosition * (corkscrew.size() - 1);
|
||||
|
||||
|
||||
if (renderModeDropdown.value() == "Noise") {
|
||||
drawNoise(now);
|
||||
} else if (renderModeDropdown.value() == "Fire") {
|
||||
drawFire(now);
|
||||
} else if (renderModeDropdown.value() == "Wave") {
|
||||
|
||||
drawWave(now);
|
||||
} else if (renderModeDropdown.value() == "Animartrix") {
|
||||
drawAnimartrix(now);
|
||||
} else {
|
||||
draw(pos);
|
||||
}
|
||||
|
||||
|
||||
// Use the new readFrom workflow:
|
||||
// 1. Read directly from the frameBuffer Grid into the corkscrew's internal buffer
|
||||
// use_multi_sampling = true will use multi-sampling to sample from the source grid,
|
||||
// this will give a little bit better accuracy and the screenmap will be more accurate.
|
||||
const bool use_multi_sampling = splatRendering;
|
||||
// corkscrew.readFrom(frameBuffer, use_multi_sampling);
|
||||
corkscrew.draw(use_multi_sampling);
|
||||
|
||||
// The corkscrew's buffer is now populated and FastLED will display it directly
|
||||
|
||||
FastLED.setBrightness(brightness.value());
|
||||
FastLED.show();
|
||||
}
|
||||
220
.pio/libdeps/esp01_1m/FastLED/examples/FestivalStick/old.h
Normal file
220
.pio/libdeps/esp01_1m/FastLED/examples/FestivalStick/old.h
Normal file
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
Festival Stick is a dense corkscrew of LEDs that is wrapped around one end of
|
||||
a wooden walking stick commonly found on amazon.A0
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "fl/assert.h"
|
||||
#include "fl/screenmap.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
|
||||
|
||||
// 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 CM_BETWEEN_LEDS 1.0 // 1cm between LEDs
|
||||
// #define CM_LED_DIAMETER 0.5 // 0.5cm LED diameter
|
||||
|
||||
UITitle festivalStickTitle("Festival Stick - Classic Version");
|
||||
UIDescription festivalStickDescription(
|
||||
"Take a wooden walking stick, wrap dense LEDs around it like a corkscrew. Super simple but very awesome looking. "
|
||||
"This classic version uses 3D Perlin noise to create organic, flowing patterns around the cylindrical surface. "
|
||||
"Assumes dense 144 LEDs/meter (288 total LEDs).");
|
||||
|
||||
// UIHelp festivalStickHelp("Festival Stick - Classic Guide");
|
||||
|
||||
// UIHelp technicalHelp("Technical Details - Classic Festival Stick");
|
||||
// UIHelp usageHelp("Usage Guide - Classic Festival Stick");
|
||||
// UIHelp physicalBuildHelp("Building Your Festival Stick");
|
||||
|
||||
|
||||
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];
|
||||
|
||||
|
||||
// fl::vector<vec3f>
|
||||
struct corkscrew_args {
|
||||
int num_leds = NUM_LEDS;
|
||||
float leds_per_turn = 15.5;
|
||||
float width_cm = 1.0;
|
||||
};
|
||||
|
||||
fl::vector<vec3f> makeCorkScrew(corkscrew_args args = corkscrew_args()) {
|
||||
// int num_leds, float leds_per_turn, float width_cm
|
||||
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 total_angle_radians = angle_per_led * num_leds;
|
||||
const float total_turns = total_angle_radians / (2.0 * PI); // total turns
|
||||
const float height_per_turn_cm = width_cm; // 10cm height per turn
|
||||
const float height_per_led =
|
||||
height_per_turn_cm /
|
||||
leds_per_turn; // this is the changing height per led.
|
||||
const float total_height =
|
||||
height_per_turn_cm * total_turns; // total height of the corkscrew
|
||||
fl::vector<vec3f> out;
|
||||
for (int i = 0; i < num_leds; i++) {
|
||||
float angle = i * angle_per_led; // angle in radians
|
||||
float height = (i / leds_per_turn) * height_per_turn_cm; // height in cm
|
||||
|
||||
// Calculate the x, y, z coordinates for the corkscrew
|
||||
float x = radius * cos(angle); // x coordinate
|
||||
float z = radius * sin(angle); // y coordinate
|
||||
float y = height; // z coordinate
|
||||
|
||||
// Store the 3D coordinates in the vector
|
||||
vec3f led_position(x, y, z);
|
||||
// screenMap.set(i, led_position);
|
||||
out.push_back(led_position);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
// - 2mm LED inner diameter
|
||||
// - 24 LEDs per turn
|
||||
// fl::ScreenMap screenMap = makeCorkScrew(NUM_LEDS,
|
||||
// 300.0f, 50.0f, 2.0f, 24.0f);
|
||||
|
||||
corkscrew_args args = corkscrew_args();
|
||||
fl::vector<vec3f> mapCorkScrew = makeCorkScrew(args);
|
||||
fl::ScreenMap screenMap;
|
||||
|
||||
|
||||
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));
|
||||
screenMap = makeScreenMap(args);
|
||||
//screenMap = ScreenMap::Circle(NUM_LEDS, 1.5f, 0.5f, 1.0f);
|
||||
auto controller = addController();
|
||||
// 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);
|
||||
// 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
|
||||
if (but.isPressed()) {
|
||||
FASTLED_WARN("Button pressed");
|
||||
} else {
|
||||
FASTLED_WARN("NOT Button pressed");
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
void showGenerative(uint32_t now) {
|
||||
// This function is called to show the generative pattern
|
||||
for (int i = 0; i < NUM_LEDS; i++) {
|
||||
// Get the 2D position of this LED from the screen map
|
||||
fl::vec3f pos = mapCorkScrew[i];
|
||||
float x = pos.x;
|
||||
float y = pos.y;
|
||||
float z = pos.z;
|
||||
|
||||
x*= 20.0f * ledsScale.value();
|
||||
y*= 20.0f * ledsScale.value();
|
||||
z*= 20.0f * ledsScale.value();
|
||||
|
||||
uint16_t noise_value = inoise16(x,y,z, now / 100);
|
||||
// Normalize the noise value to 0-255
|
||||
uint8_t brightness = map(noise_value, 0, 65535, 0, 255);
|
||||
// Create a hue that changes with position and time
|
||||
uint8_t sat = int32_t((x * 10 + y * 5 + now / 5)) % 256;
|
||||
// Set the color
|
||||
leds[i] = CHSV(170, sat, fl::clamp(255- sat, 64, 255));
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
uint32_t now = millis();
|
||||
fl::clear(leds);
|
||||
showGenerative(now);
|
||||
FastLED.show();
|
||||
}
|
||||
111
.pio/libdeps/esp01_1m/FastLED/examples/Fire2012/Fire2012.ino
Normal file
111
.pio/libdeps/esp01_1m/FastLED/examples/Fire2012/Fire2012.ino
Normal file
@@ -0,0 +1,111 @@
|
||||
/// @file Fire2012.ino
|
||||
/// @brief Simple one-dimensional fire animation
|
||||
/// @example Fire2012.ino
|
||||
|
||||
#include <FastLED.h>
|
||||
|
||||
#define LED_PIN 5
|
||||
#define COLOR_ORDER GRB
|
||||
#define CHIPSET WS2811
|
||||
#define NUM_LEDS 30
|
||||
|
||||
#define BRIGHTNESS 200
|
||||
#define FRAMES_PER_SECOND 60
|
||||
|
||||
bool gReverseDirection = false;
|
||||
|
||||
CRGB leds[NUM_LEDS];
|
||||
|
||||
// Forward declaration
|
||||
void Fire2012();
|
||||
|
||||
void setup() {
|
||||
delay(3000); // sanity delay
|
||||
FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
|
||||
FastLED.setBrightness( BRIGHTNESS );
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Add entropy to random number generator; we use a lot of it.
|
||||
random16_add_entropy( random16());
|
||||
|
||||
Fire2012(); // run simulation frame
|
||||
|
||||
FastLED.show(); // display this frame
|
||||
FastLED.delay(1000 / FRAMES_PER_SECOND);
|
||||
}
|
||||
|
||||
|
||||
// Fire2012 by Mark Kriegsman, July 2012
|
||||
// as part of "Five Elements" shown here: http://youtu.be/knWiGsmgycY
|
||||
////
|
||||
// This basic one-dimensional 'fire' simulation works roughly as follows:
|
||||
// There's a underlying array of 'heat' cells, that model the temperature
|
||||
// at each point along the line. Every cycle through the simulation,
|
||||
// four steps are performed:
|
||||
// 1) All cells cool down a little bit, losing heat to the air
|
||||
// 2) The heat from each cell drifts 'up' and diffuses a little
|
||||
// 3) Sometimes randomly new 'sparks' of heat are added at the bottom
|
||||
// 4) The heat from each cell is rendered as a color into the leds array
|
||||
// The heat-to-color mapping uses a black-body radiation approximation.
|
||||
//
|
||||
// Temperature is in arbitrary units from 0 (cold black) to 255 (white hot).
|
||||
//
|
||||
// This simulation scales it self a bit depending on NUM_LEDS; it should look
|
||||
// "OK" on anywhere from 20 to 100 LEDs without too much tweaking.
|
||||
//
|
||||
// I recommend running this simulation at anywhere from 30-100 frames per second,
|
||||
// meaning an interframe delay of about 10-35 milliseconds.
|
||||
//
|
||||
// Looks best on a high-density LED setup (60+ pixels/meter).
|
||||
//
|
||||
//
|
||||
// There are two main parameters you can play with to control the look and
|
||||
// feel of your fire: COOLING (used in step 1 above), and SPARKING (used
|
||||
// in step 3 above).
|
||||
//
|
||||
// COOLING: How much does the air cool as it rises?
|
||||
// Less cooling = taller flames. More cooling = shorter flames.
|
||||
// Default 50, suggested range 20-100
|
||||
#define COOLING 55
|
||||
|
||||
// SPARKING: What chance (out of 255) is there that a new spark will be lit?
|
||||
// Higher chance = more roaring fire. Lower chance = more flickery fire.
|
||||
// Default 120, suggested range 50-200.
|
||||
#define SPARKING 120
|
||||
|
||||
|
||||
void Fire2012()
|
||||
{
|
||||
// Array of temperature readings at each simulation cell
|
||||
static uint8_t heat[NUM_LEDS];
|
||||
|
||||
// Step 1. Cool down every cell a little
|
||||
for( int i = 0; i < NUM_LEDS; i++) {
|
||||
heat[i] = qsub8( heat[i], random8(0, ((COOLING * 10) / NUM_LEDS) + 2));
|
||||
}
|
||||
|
||||
// Step 2. Heat from each cell drifts 'up' and diffuses a little
|
||||
for( int k= NUM_LEDS - 1; k >= 2; k--) {
|
||||
heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
|
||||
}
|
||||
|
||||
// Step 3. Randomly ignite new 'sparks' of heat near the bottom
|
||||
if( random8() < SPARKING ) {
|
||||
int y = random8(7);
|
||||
heat[y] = qadd8( heat[y], random8(160,255) );
|
||||
}
|
||||
|
||||
// Step 4. Map from heat cells to LED colors
|
||||
for( int j = 0; j < NUM_LEDS; j++) {
|
||||
CRGB color = HeatColor( heat[j]);
|
||||
int pixelnumber;
|
||||
if( gReverseDirection ) {
|
||||
pixelnumber = (NUM_LEDS-1) - j;
|
||||
} else {
|
||||
pixelnumber = j;
|
||||
}
|
||||
leds[pixelnumber] = color;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
/// @file Fire2012WithPalette.ino
|
||||
/// @brief Simple one-dimensional fire animation with a programmable color palette
|
||||
/// @example Fire2012WithPalette.ino
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <FastLED.h>
|
||||
|
||||
#define LED_PIN 5
|
||||
#define COLOR_ORDER GRB
|
||||
#define CHIPSET WS2811
|
||||
#define NUM_LEDS 30
|
||||
|
||||
#define BRIGHTNESS 200
|
||||
#define FRAMES_PER_SECOND 60
|
||||
|
||||
bool gReverseDirection = false;
|
||||
|
||||
CRGB leds[NUM_LEDS];
|
||||
|
||||
CRGBPalette16 gPal;
|
||||
|
||||
// Fire2012 by Mark Kriegsman, July 2012
|
||||
// as part of "Five Elements" shown here: http://youtu.be/knWiGsmgycY
|
||||
////
|
||||
// This basic one-dimensional 'fire' simulation works roughly as follows:
|
||||
// There's a underlying array of 'heat' cells, that model the temperature
|
||||
// at each point along the line. Every cycle through the simulation,
|
||||
// four steps are performed:
|
||||
// 1) All cells cool down a little bit, losing heat to the air
|
||||
// 2) The heat from each cell drifts 'up' and diffuses a little
|
||||
// 3) Sometimes randomly new 'sparks' of heat are added at the bottom
|
||||
// 4) The heat from each cell is rendered as a color into the leds array
|
||||
// The heat-to-color mapping uses a black-body radiation approximation.
|
||||
//
|
||||
// Temperature is in arbitrary units from 0 (cold black) to 255 (white hot).
|
||||
//
|
||||
// This simulation scales it self a bit depending on NUM_LEDS; it should look
|
||||
// "OK" on anywhere from 20 to 100 LEDs without too much tweaking.
|
||||
//
|
||||
// I recommend running this simulation at anywhere from 30-100 frames per second,
|
||||
// meaning an interframe delay of about 10-35 milliseconds.
|
||||
//
|
||||
// Looks best on a high-density LED setup (60+ pixels/meter).
|
||||
//
|
||||
//
|
||||
// There are two main parameters you can play with to control the look and
|
||||
// feel of your fire: COOLING (used in step 1 above), and SPARKING (used
|
||||
// in step 3 above).
|
||||
//
|
||||
// COOLING: How much does the air cool as it rises?
|
||||
// Less cooling = taller flames. More cooling = shorter flames.
|
||||
// Default 55, suggested range 20-100
|
||||
#define COOLING 55
|
||||
|
||||
// SPARKING: What chance (out of 255) is there that a new spark will be lit?
|
||||
// Higher chance = more roaring fire. Lower chance = more flickery fire.
|
||||
// Default 120, suggested range 50-200.
|
||||
#define SPARKING 120
|
||||
|
||||
|
||||
void Fire2012WithPalette()
|
||||
{
|
||||
// Array of temperature readings at each simulation cell
|
||||
static uint8_t heat[NUM_LEDS];
|
||||
|
||||
// Step 1. Cool down every cell a little
|
||||
for( int i = 0; i < NUM_LEDS; i++) {
|
||||
heat[i] = qsub8( heat[i], random8(0, ((COOLING * 10) / NUM_LEDS) + 2));
|
||||
}
|
||||
|
||||
// Step 2. Heat from each cell drifts 'up' and diffuses a little
|
||||
for( int k= NUM_LEDS - 1; k >= 2; k--) {
|
||||
heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
|
||||
}
|
||||
|
||||
// Step 3. Randomly ignite new 'sparks' of heat near the bottom
|
||||
if( random8() < SPARKING ) {
|
||||
int y = random8(7);
|
||||
heat[y] = qadd8( heat[y], random8(160,255) );
|
||||
}
|
||||
|
||||
// Step 4. Map from heat cells to LED colors
|
||||
for( int j = 0; j < NUM_LEDS; j++) {
|
||||
// Scale the heat value from 0-255 down to 0-240
|
||||
// for best results with color palettes.
|
||||
uint8_t colorindex = scale8( heat[j], 240);
|
||||
CRGB color = ColorFromPalette( gPal, colorindex);
|
||||
int pixelnumber;
|
||||
if( gReverseDirection ) {
|
||||
pixelnumber = (NUM_LEDS-1) - j;
|
||||
} else {
|
||||
pixelnumber = j;
|
||||
}
|
||||
leds[pixelnumber] = color;
|
||||
}
|
||||
}
|
||||
|
||||
// Fire2012 with programmable Color Palette
|
||||
//
|
||||
// This code is the same fire simulation as the original "Fire2012",
|
||||
// but each heat cell's temperature is translated to color through a FastLED
|
||||
// programmable color palette, instead of through the "HeatColor(...)" function.
|
||||
//
|
||||
// Four different static color palettes are provided here, plus one dynamic one.
|
||||
//
|
||||
// The three static ones are:
|
||||
// 1. the FastLED built-in HeatColors_p -- this is the default, and it looks
|
||||
// pretty much exactly like the original Fire2012.
|
||||
//
|
||||
// To use any of the other palettes below, just "uncomment" the corresponding code.
|
||||
//
|
||||
// 2. a gradient from black to red to yellow to white, which is
|
||||
// visually similar to the HeatColors_p, and helps to illustrate
|
||||
// what the 'heat colors' palette is actually doing,
|
||||
// 3. a similar gradient, but in blue colors rather than red ones,
|
||||
// i.e. from black to blue to aqua to white, which results in
|
||||
// an "icy blue" fire effect,
|
||||
// 4. a simplified three-step gradient, from black to red to white, just to show
|
||||
// that these gradients need not have four components; two or
|
||||
// three are possible, too, even if they don't look quite as nice for fire.
|
||||
//
|
||||
// The dynamic palette shows how you can change the basic 'hue' of the
|
||||
// color palette every time through the loop, producing "rainbow fire".
|
||||
|
||||
void setup() {
|
||||
delay(3000); // sanity delay
|
||||
FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
|
||||
FastLED.setBrightness( BRIGHTNESS );
|
||||
|
||||
// This first palette is the basic 'black body radiation' colors,
|
||||
// which run from black to red to bright yellow to white.
|
||||
gPal = HeatColors_p;
|
||||
|
||||
// These are other ways to set up the color palette for the 'fire'.
|
||||
// First, a gradient from black to red to yellow to white -- similar to HeatColors_p
|
||||
// gPal = CRGBPalette16( CRGB::Black, CRGB::Red, CRGB::Yellow, CRGB::White);
|
||||
|
||||
// Second, this palette is like the heat colors, but blue/aqua instead of red/yellow
|
||||
// gPal = CRGBPalette16( CRGB::Black, CRGB::Blue, CRGB::Aqua, CRGB::White);
|
||||
|
||||
// Third, here's a simpler, three-step gradient, from black to red to white
|
||||
// gPal = CRGBPalette16( CRGB::Black, CRGB::Red, CRGB::White);
|
||||
Serial.println("setup");
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Add entropy to random number generator; we use a lot of it.
|
||||
random16_add_entropy( random16());
|
||||
|
||||
// Fourth, the most sophisticated: this one sets up a new palette every
|
||||
// time through the loop, based on a hue that changes every time.
|
||||
// The palette is a gradient from black, to a dark color based on the hue,
|
||||
// to a light color based on the hue, to white.
|
||||
//
|
||||
// static uint8_t hue = 0;
|
||||
// hue++;
|
||||
// CRGB darkcolor = CHSV(hue,255,192); // pure hue, three-quarters brightness
|
||||
// CRGB lightcolor = CHSV(hue,128,255); // half 'whitened', full brightness
|
||||
// gPal = CRGBPalette16( CRGB::Black, darkcolor, lightcolor, CRGB::White);
|
||||
|
||||
|
||||
Fire2012WithPalette(); // run simulation frame, using palette colors
|
||||
|
||||
FastLED.show(); // display this frame
|
||||
FastLED.delay(1000 / FRAMES_PER_SECOND);
|
||||
}
|
||||
262
.pio/libdeps/esp01_1m/FastLED/examples/Fire2023/Fire2023.h
Normal file
262
.pio/libdeps/esp01_1m/FastLED/examples/Fire2023/Fire2023.h
Normal file
@@ -0,0 +1,262 @@
|
||||
/// @file Fire2023.ino
|
||||
/// @brief Enhanced fire effect with ScreenMap
|
||||
/// @example Fire2023.ino
|
||||
///
|
||||
/// 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`
|
||||
/// 4. When the compiler is done a web page will open.
|
||||
|
||||
/*This is a fire effect based on the famous Fire2012; but with various small improvements.
|
||||
Perlin noise is being used to make a fire layer and a smoke layer;
|
||||
and the overlay of both can make a quite realistic effect.
|
||||
|
||||
The speed of both need to be adapted to the matrix size and width:
|
||||
* Super small matrices (like 3x3 led) don't need the smoke
|
||||
* medium sized matrices (8x8 for example) profit from fine tuning both Fire Speed/scale as well as Smoke speed/scale
|
||||
|
||||
This code was adapted for a matrix with just four LED columns in 90° around a core and a height of 28.
|
||||
|
||||
Right at the bottom of the code, you find a translation matrix that needs to be adapted to your set up. I included
|
||||
a link to a helpful page for this.
|
||||
|
||||
@repo https://github.com/Anderas2/Fire2023
|
||||
@author https://github.com/Anderas2
|
||||
|
||||
Demo: https://www.youtube.com/shorts/a_Wr0q9YQs4
|
||||
*/
|
||||
|
||||
|
||||
#include "FastLED.h"
|
||||
#include "fl/xymap.h"
|
||||
#include "fl/screenmap.h"
|
||||
#include "fl/vector.h"
|
||||
|
||||
using namespace fl;
|
||||
|
||||
|
||||
// matrix size
|
||||
#define WIDTH 4
|
||||
#define HEIGHT 28
|
||||
#define CentreX (WIDTH / 2) - 1
|
||||
#define CentreY (HEIGHT / 2) - 1
|
||||
|
||||
// NUM_LEDS = WIDTH * HEIGHT
|
||||
#define PIXELPIN 3 // universal pin that works on all platforms
|
||||
#define NUM_LEDS 120
|
||||
#define LAST_VISIBLE_LED 119
|
||||
|
||||
|
||||
// Fire properties
|
||||
#define BRIGHTNESS 255
|
||||
#define FIRESPEED 17
|
||||
#define FLAMEHEIGHT 3.8 // the higher the value, the higher the flame
|
||||
#define FIRENOISESCALE 125 // small values, softer fire. Big values, blink fire. 0-255
|
||||
|
||||
// Smoke screen properties
|
||||
// The smoke screen works best for big fire effects. It effectively cuts of a part of the flames
|
||||
// from the rest, sometimes; which looks very much fire-like. For small fire effects with low
|
||||
// LED count in the height, it doesn't help
|
||||
// speed must be a little different and faster from Firespeed, to be visible.
|
||||
// Dimmer should be somewhere in the middle for big fires, and low for small fires.
|
||||
#define SMOKESPEED 25 // how fast the perlin noise is parsed for the smoke
|
||||
#define SMOKENOISE_DIMMER 250 // thickness of smoke: the lower the value, the brighter the flames. 0-255
|
||||
#define SMOKENOISESCALE 125 // small values, softer smoke. Big values, blink smoke. 0-255
|
||||
|
||||
CRGB leds[NUM_LEDS];
|
||||
|
||||
// fire palette roughly like matlab "hot" colormap
|
||||
// This was one of the most important parts to improve - fire color makes fire impression.
|
||||
// position, r, g, b value.
|
||||
// max value for "position" is BRIGHTNESS
|
||||
DEFINE_GRADIENT_PALETTE(hot_gp) {
|
||||
27, 0, 0, 0, // black
|
||||
28, 140, 40, 0, // red
|
||||
30, 205, 80, 0, // orange
|
||||
155, 255, 100, 0,
|
||||
210, 255, 200, 0, // yellow
|
||||
255, 255, 255, 255 // white
|
||||
};
|
||||
CRGBPalette32 hotPalette = hot_gp;
|
||||
|
||||
// Map XY coordinates to numbers on the LED strip
|
||||
uint8_t XY (uint8_t x, uint8_t y);
|
||||
|
||||
|
||||
// parameters and buffer for the noise array
|
||||
#define NUM_LAYERS 2
|
||||
// two layers of perlin noise make the fire effect
|
||||
#define FIRENOISE 0
|
||||
#define SMOKENOISE 1
|
||||
uint32_t x[NUM_LAYERS];
|
||||
uint32_t y[NUM_LAYERS];
|
||||
uint32_t z[NUM_LAYERS];
|
||||
uint32_t scale_x[NUM_LAYERS];
|
||||
uint32_t scale_y[NUM_LAYERS];
|
||||
|
||||
uint8_t noise[NUM_LAYERS][WIDTH][HEIGHT];
|
||||
uint8_t noise2[NUM_LAYERS][WIDTH][HEIGHT];
|
||||
|
||||
uint8_t heat[NUM_LEDS];
|
||||
|
||||
|
||||
ScreenMap makeScreenMap();
|
||||
|
||||
void setup() {
|
||||
|
||||
//Serial.begin(115200);
|
||||
// Adjust this for you own setup. Use the hardware SPI pins if possible.
|
||||
// On Teensy 3.1/3.2 the pins are 11 & 13
|
||||
// Details here: https://github.com/FastLED/FastLED/wiki/SPI-Hardware-or-Bit-banging
|
||||
// In case you see flickering / glitching leds, reduce the data rate to 12 MHZ or less
|
||||
auto screenMap = makeScreenMap();
|
||||
FastLED.addLeds<NEOPIXEL, PIXELPIN>(leds, NUM_LEDS).setScreenMap(screenMap); // Pin für Neopixel
|
||||
FastLED.setBrightness(BRIGHTNESS);
|
||||
FastLED.setDither(DISABLE_DITHER);
|
||||
}
|
||||
|
||||
void Fire2023(uint32_t now);
|
||||
|
||||
void loop() {
|
||||
EVERY_N_MILLISECONDS(8) {
|
||||
Fire2023(millis());
|
||||
}
|
||||
FastLED.show();
|
||||
}
|
||||
|
||||
ScreenMap makeScreenMap() {
|
||||
fl::vector<vec2f> lut;
|
||||
for (uint16_t y = 0; y < WIDTH; y++) {
|
||||
for (uint16_t x = 0; x < HEIGHT; x++) {
|
||||
vec2f xy = {float(x) * 3, float(y) * 20};
|
||||
lut.push_back(xy);
|
||||
}
|
||||
}
|
||||
return ScreenMap(lut.data(), lut.size(), 1);
|
||||
}
|
||||
|
||||
void Fire2023(uint32_t now) {
|
||||
// some changing values
|
||||
// these values are produced by perlin noise to add randomness and smooth transitions
|
||||
uint16_t ctrl1 = inoise16(11 * now, 0, 0);
|
||||
uint16_t ctrl2 = inoise16(13 * now, 100000, 100000);
|
||||
uint16_t ctrl = ((ctrl1 + ctrl2) >> 1);
|
||||
|
||||
// parameters for the fire heat map
|
||||
x[FIRENOISE] = 3 * ctrl * FIRESPEED;
|
||||
y[FIRENOISE] = 20 * now * FIRESPEED;
|
||||
z[FIRENOISE] = 5 * now * FIRESPEED;
|
||||
scale_x[FIRENOISE] = scale8(ctrl1, FIRENOISESCALE);
|
||||
scale_y[FIRENOISE] = scale8(ctrl2, FIRENOISESCALE);
|
||||
|
||||
//calculate the perlin noise data for the fire
|
||||
for (uint8_t x_count = 0; x_count < WIDTH; x_count++) {
|
||||
uint32_t xoffset = scale_x[FIRENOISE] * (x_count - CentreX);
|
||||
for (uint8_t y_count = 0; y_count < HEIGHT; y_count++) {
|
||||
uint32_t yoffset = scale_y[FIRENOISE] * (y_count - CentreY);
|
||||
uint16_t data = ((inoise16(x[FIRENOISE] + xoffset, y[FIRENOISE] + yoffset, z[FIRENOISE])) + 1);
|
||||
noise[FIRENOISE][x_count][y_count] = data >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
// parameters for the smoke map
|
||||
x[SMOKENOISE] = 3 * ctrl * SMOKESPEED;
|
||||
y[SMOKENOISE] = 20 * now * SMOKESPEED;
|
||||
z[SMOKENOISE] = 5 * now * SMOKESPEED;
|
||||
scale_x[SMOKENOISE] = scale8(ctrl1, SMOKENOISESCALE);
|
||||
scale_y[SMOKENOISE] = scale8(ctrl2, SMOKENOISESCALE);
|
||||
|
||||
//calculate the perlin noise data for the smoke
|
||||
for (uint8_t x_count = 0; x_count < WIDTH; x_count++) {
|
||||
uint32_t xoffset = scale_x[SMOKENOISE] * (x_count - CentreX);
|
||||
for (uint8_t y_count = 0; y_count < HEIGHT; y_count++) {
|
||||
uint32_t yoffset = scale_y[SMOKENOISE] * (y_count - CentreY);
|
||||
uint16_t data = ((inoise16(x[SMOKENOISE] + xoffset, y[SMOKENOISE] + yoffset, z[SMOKENOISE])) + 1);
|
||||
noise[SMOKENOISE][x_count][y_count] = data / SMOKENOISE_DIMMER;
|
||||
}
|
||||
}
|
||||
|
||||
//copy everything one line up
|
||||
for (uint8_t y = 0; y < HEIGHT - 1; y++) {
|
||||
for (uint8_t x = 0; x < WIDTH; x++) {
|
||||
heat[XY(x, y)] = heat[XY(x, y + 1)];
|
||||
}
|
||||
}
|
||||
|
||||
// draw lowest line - seed the fire where it is brightest and hottest
|
||||
for (uint8_t x = 0; x < WIDTH; x++) {
|
||||
heat[XY(x, HEIGHT-1)] = noise[FIRENOISE][WIDTH - x][CentreX];
|
||||
//if (heat[XY(x, HEIGHT-1)] < 200) heat[XY(x, HEIGHT-1)] = 150;
|
||||
}
|
||||
|
||||
// dim the flames based on FIRENOISE noise.
|
||||
// if the FIRENOISE noise is strong, the led goes out fast
|
||||
// if the FIRENOISE noise is weak, the led stays on stronger.
|
||||
// once the heat is gone, it stays dark.
|
||||
for (uint8_t y = 0; y < HEIGHT - 1; y++) {
|
||||
for (uint8_t x = 0; x < WIDTH; x++) {
|
||||
uint8_t dim = noise[FIRENOISE][x][y];
|
||||
// high value in FLAMEHEIGHT = less dimming = high flames
|
||||
dim = dim / FLAMEHEIGHT;
|
||||
dim = 255 - dim;
|
||||
heat[XY(x, y)] = scale8(heat[XY(x, y)] , dim);
|
||||
|
||||
// map the colors based on heatmap
|
||||
// use the heat map to set the color of the LED from the "hot" palette
|
||||
// whichpalette position brightness blend or not
|
||||
leds[XY(x, y)] = ColorFromPalette(hotPalette, heat[XY(x, y)], heat[XY(x, y)], LINEARBLEND);
|
||||
|
||||
// dim the result based on SMOKENOISE noise
|
||||
// this is not saved in the heat map - the flame may dim away and come back
|
||||
// next iteration.
|
||||
leds[XY(x, y)].nscale8(noise[SMOKENOISE][x][y]);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Physical layout of LED strip ****************************/
|
||||
uint8_t XY (uint8_t x, uint8_t y) {
|
||||
// any out of bounds address maps to the first hidden pixel
|
||||
// https://macetech.github.io/FastLED-XY-Map-Generator/
|
||||
if ( (x >= WIDTH) || (y >= HEIGHT) ) {
|
||||
return (LAST_VISIBLE_LED + 1);
|
||||
}
|
||||
const uint8_t XYTable[] = {
|
||||
25, 26, 81, 82,
|
||||
25, 27, 81, 83,
|
||||
25, 28, 80, 84,
|
||||
24, 29, 79, 85,
|
||||
23, 30, 78, 86,
|
||||
22, 31, 77, 87,
|
||||
21, 32, 76, 88,
|
||||
20, 33, 75, 89,
|
||||
19, 34, 74, 90,
|
||||
18, 35, 73, 91,
|
||||
17, 36, 72, 92,
|
||||
16, 37, 71, 93,
|
||||
15, 38, 70, 94,
|
||||
14, 39, 69, 95,
|
||||
13, 40, 68, 96,
|
||||
12, 41, 67, 97,
|
||||
11, 42, 66, 98,
|
||||
10, 43, 65, 99,
|
||||
9, 44, 64, 100,
|
||||
8, 45, 63, 101,
|
||||
7, 46, 62, 102,
|
||||
6, 47, 61, 103,
|
||||
5, 48, 60, 104,
|
||||
4, 49, 59, 105,
|
||||
3, 50, 58, 106,
|
||||
2, 51, 57, 107,
|
||||
1, 52, 56, 108,
|
||||
0, 53, 55, 109
|
||||
};
|
||||
|
||||
uint8_t i = (y * WIDTH) + x;
|
||||
uint8_t j = XYTable[i];
|
||||
return j;
|
||||
}
|
||||
|
||||
|
||||
17
.pio/libdeps/esp01_1m/FastLED/examples/Fire2023/Fire2023.ino
Normal file
17
.pio/libdeps/esp01_1m/FastLED/examples/Fire2023/Fire2023.ino
Normal file
@@ -0,0 +1,17 @@
|
||||
#include "fl/sketch_macros.h"
|
||||
|
||||
#if SKETCH_HAS_LOTS_OF_MEMORY
|
||||
|
||||
#include "./Fire2023.h"
|
||||
|
||||
#else
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.println("Not enough memory");
|
||||
delay(1000);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,215 @@
|
||||
|
||||
/*
|
||||
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>();
|
||||
FL_UNUSED(z); // Suppress unused variable warning
|
||||
|
||||
// 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();
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#include "FastLED.h" // Main FastLED library for controlling LEDs
|
||||
|
||||
#if !SKETCH_HAS_LOTS_OF_MEMORY
|
||||
// Platform does not have enough memory
|
||||
void setup() {}
|
||||
void loop() {}
|
||||
#else
|
||||
#include "FireCylinder.h"
|
||||
#endif
|
||||
200
.pio/libdeps/esp01_1m/FastLED/examples/FireMatrix/FireMatrix.h
Normal file
200
.pio/libdeps/esp01_1m/FastLED/examples/FireMatrix/FireMatrix.h
Normal 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
|
||||
@@ -0,0 +1,9 @@
|
||||
#include "FastLED.h" // Main FastLED library for controlling LEDs
|
||||
|
||||
#if !SKETCH_HAS_LOTS_OF_MEMORY
|
||||
// Platform does not have enough memory
|
||||
void setup() {}
|
||||
void loop() {}
|
||||
#else
|
||||
#include "FireMatrix.h"
|
||||
#endif
|
||||
@@ -0,0 +1,96 @@
|
||||
/// @file FirstLight.ino
|
||||
/// @brief Animate a white dot moving along a strip of LEDs
|
||||
/// @example FirstLight.ino
|
||||
|
||||
// Use if you want to force the software SPI subsystem to be used for some reason (generally, you don't)
|
||||
// #define FASTLED_FORCE_SOFTWARE_SPI
|
||||
// Use if you want to force non-accelerated pin access (hint: you really don't, it breaks lots of things)
|
||||
// #define FASTLED_FORCE_SOFTWARE_SPI
|
||||
// #define FASTLED_FORCE_SOFTWARE_PINS
|
||||
#include <FastLED.h>
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Move a white dot along the strip of leds. This program simply shows how to configure the leds,
|
||||
// and then how to turn a single pixel white and then off, moving down the line of pixels.
|
||||
//
|
||||
|
||||
// How many leds are in the strip?
|
||||
#define NUM_LEDS 60
|
||||
|
||||
// For led chips like WS2812, which have a data line, ground, and power, you just
|
||||
// need to define DATA_PIN. For led chipsets that are SPI based (four wires - data, clock,
|
||||
// ground, and power), like the LPD8806 define both DATA_PIN and CLOCK_PIN
|
||||
// Clock pin only needed for SPI based chipsets when not using hardware SPI
|
||||
#define DATA_PIN 3
|
||||
#define CLOCK_PIN 13
|
||||
|
||||
// This is an array of leds. One item for each led in your strip.
|
||||
CRGB leds[NUM_LEDS];
|
||||
|
||||
// This function sets up the ledsand tells the controller about them
|
||||
void setup() {
|
||||
// sanity check delay - allows reprogramming if accidently blowing power w/leds
|
||||
delay(2000);
|
||||
|
||||
// Uncomment/edit one of the following lines for your leds arrangement.
|
||||
// ## Clockless types ##
|
||||
// FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS); // GRB ordering is assumed
|
||||
// FastLED.addLeds<SM16703, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<TM1829, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<TM1812, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<TM1809, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<TM1804, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<TM1803, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<UCS1903, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<UCS1903B, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<UCS1904, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<UCS2903, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<WS2812, DATA_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
|
||||
// FastLED.addLeds<WS2852, DATA_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
|
||||
// FastLED.addLeds<WS2812B, DATA_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
|
||||
// FastLED.addLeds<GS1903, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<SK6812, DATA_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
|
||||
// FastLED.addLeds<SK6822, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<APA106, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<PL9823, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<SK6822, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<WS2813, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<APA104, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<WS2811_400, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<GE8822, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<GW6205, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<GW6205_400, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<LPD1886, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<LPD1886_8BIT, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||
// ## Clocked (SPI) types ##
|
||||
// FastLED.addLeds<LPD6803, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
|
||||
// FastLED.addLeds<LPD8806, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
|
||||
// FastLED.addLeds<WS2801, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<WS2803, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<SM16716, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
|
||||
// FastLED.addLeds<P9813, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS); // BGR ordering is typical
|
||||
// FastLED.addLeds<DOTSTAR, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS); // BGR ordering is typical
|
||||
// FastLED.addLeds<APA102, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS); // BGR ordering is typical
|
||||
// FastLED.addLeds<SK9822, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS); // BGR ordering is typical
|
||||
}
|
||||
|
||||
// This function runs over and over, and is where you do the magic to light
|
||||
// your leds.
|
||||
void loop() {
|
||||
// Move a single white led
|
||||
for(int whiteLed = 0; whiteLed < NUM_LEDS; whiteLed = whiteLed + 1) {
|
||||
// Turn our current led on to white, then show the leds
|
||||
leds[whiteLed] = CRGB::White;
|
||||
|
||||
// Show the leds (only one of which is set to white, from above)
|
||||
FastLED.show();
|
||||
|
||||
// Wait a little bit
|
||||
delay(100);
|
||||
|
||||
// Turn our current led back to black for the next loop around
|
||||
leds[whiteLed] = CRGB::Black;
|
||||
}
|
||||
}
|
||||
41
.pio/libdeps/esp01_1m/FastLED/examples/FxCylon/FxCylon.ino
Normal file
41
.pio/libdeps/esp01_1m/FastLED/examples/FxCylon/FxCylon.ino
Normal file
@@ -0,0 +1,41 @@
|
||||
/// @file FxCylon.ino
|
||||
/// @brief Cylon eye effect with ScreenMap
|
||||
/// @example FxCylon.ino
|
||||
///
|
||||
/// 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`
|
||||
/// 4. When the compiler is done a web page will open.
|
||||
|
||||
#include <FastLED.h>
|
||||
#include "fx/1d/cylon.h"
|
||||
#include "fl/screenmap.h"
|
||||
|
||||
using namespace fl;
|
||||
|
||||
// How many leds in your strip?
|
||||
#define NUM_LEDS 64
|
||||
|
||||
// For led chips like Neopixels, which have a data line, ground, and power, you just
|
||||
// need to define DATA_PIN. For led chipsets that are SPI based (four wires - data, clock,
|
||||
// ground, and power).
|
||||
#define DATA_PIN 2
|
||||
|
||||
// Define the array of leds
|
||||
CRGB leds[NUM_LEDS];
|
||||
|
||||
// Create a Cylon instance
|
||||
Cylon cylon(NUM_LEDS);
|
||||
|
||||
void setup() {
|
||||
ScreenMap screenMap = ScreenMap::DefaultStrip(NUM_LEDS, 1.5f, 0.5f);
|
||||
FastLED.addLeds<WS2812,DATA_PIN,RGB>(leds,NUM_LEDS).setRgbw().setScreenMap(screenMap);
|
||||
FastLED.setBrightness(84);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
cylon.draw(Fx::DrawContext(millis(), leds));
|
||||
FastLED.show();
|
||||
delay(cylon.delay_ms);
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/// @file FxDemoReel100.ino
|
||||
/// @brief DemoReel100 effects collection with ScreenMap
|
||||
/// @example FxDemoReel100.ino
|
||||
///
|
||||
/// 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`
|
||||
/// 4. When the compiler is done a web page will open.
|
||||
|
||||
#include <FastLED.h>
|
||||
#include "fx/1d/demoreel100.h"
|
||||
#include "fl/screenmap.h"
|
||||
#include "defs.h" // for NUM_LEDS
|
||||
|
||||
|
||||
#if !HAS_ENOUGH_MEMORY
|
||||
void setup() {}
|
||||
void loop() {}
|
||||
#else
|
||||
|
||||
|
||||
using namespace fl;
|
||||
|
||||
#define DATA_PIN 3
|
||||
//#define CLK_PIN 4
|
||||
#define LED_TYPE WS2811
|
||||
#define COLOR_ORDER GRB
|
||||
#define NUM_LEDS 64
|
||||
|
||||
CRGB leds[NUM_LEDS];
|
||||
|
||||
#define BRIGHTNESS 96
|
||||
#define FRAMES_PER_SECOND 120
|
||||
#define USES_RGBW 0
|
||||
|
||||
#if USES_RGBW
|
||||
Rgbw rgbwMode = RgbwDefault();
|
||||
#else
|
||||
Rgbw rgbwMode = RgbwInvalid(); // No RGBW mode, just use RGB.
|
||||
#endif
|
||||
|
||||
DemoReel100Ptr demoReel = fl::make_shared<DemoReel100>(NUM_LEDS);
|
||||
|
||||
void setup() {
|
||||
ScreenMap screenMap = ScreenMap::DefaultStrip(NUM_LEDS);
|
||||
|
||||
// tell FastLED about the LED strip configuration
|
||||
FastLED.addLeds<LED_TYPE,DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS, 2.0f)
|
||||
.setCorrection(TypicalLEDStrip)
|
||||
.setScreenMap(screenMap)
|
||||
.setRgbw(rgbwMode);
|
||||
|
||||
// set master brightness control
|
||||
FastLED.setBrightness(BRIGHTNESS);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Run the DemoReel100 draw function
|
||||
demoReel->draw(Fx::DrawContext(millis(), leds));
|
||||
|
||||
// send the 'leds' array out to the actual LED strip
|
||||
FastLED.show();
|
||||
// insert a delay to keep the framerate modest
|
||||
FastLED.delay(1000/FRAMES_PER_SECOND);
|
||||
}
|
||||
|
||||
|
||||
#endif // HAS_ENOUGH_MEMORY
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
// if attiny85 or attiny88, use less leds so this example can compile.
|
||||
#if defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny88__)
|
||||
#define HAS_ENOUGH_MEMORY 0
|
||||
#else
|
||||
#define HAS_ENOUGH_MEMORY 1
|
||||
#endif
|
||||
79
.pio/libdeps/esp01_1m/FastLED/examples/FxEngine/FxEngine.ino
Normal file
79
.pio/libdeps/esp01_1m/FastLED/examples/FxEngine/FxEngine.ino
Normal file
@@ -0,0 +1,79 @@
|
||||
/// @file FxEngine.ino
|
||||
/// @brief Demonstrates FxEngine for switching between effects
|
||||
/// @example FxEngine.ino
|
||||
///
|
||||
/// 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`
|
||||
/// 4. When the compiler is done a web page will open.
|
||||
|
||||
#include <FastLED.h>
|
||||
using namespace fl;
|
||||
|
||||
#if defined(__AVR__)
|
||||
// __AVR__: Not enough memory enough for the FxEngine, so skipping this example
|
||||
void setup() {}
|
||||
void loop() {}
|
||||
|
||||
#else
|
||||
|
||||
#include "fx/2d/noisepalette.h"
|
||||
#include "fx/2d/animartrix.hpp"
|
||||
#include "fx/fx_engine.h"
|
||||
#include "fl/ui.h"
|
||||
|
||||
#define LED_PIN 2
|
||||
#define BRIGHTNESS 96
|
||||
#define LED_TYPE WS2811
|
||||
#define COLOR_ORDER GRB
|
||||
|
||||
#define MATRIX_WIDTH 22
|
||||
#define MATRIX_HEIGHT 22
|
||||
|
||||
#define NUM_LEDS (MATRIX_WIDTH * MATRIX_HEIGHT)
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#define IS_SERPINTINE false
|
||||
#else
|
||||
#define IS_SERPINTINE true
|
||||
#endif
|
||||
|
||||
|
||||
UISlider SCALE("SCALE", 20, 20, 100);
|
||||
UISlider SPEED("SPEED", 30, 20, 100);
|
||||
|
||||
CRGB leds[NUM_LEDS];
|
||||
XYMap xyMap(MATRIX_WIDTH, MATRIX_HEIGHT, IS_SERPINTINE); // No serpentine
|
||||
NoisePalette noisePalette1(xyMap);
|
||||
NoisePalette noisePalette2(xyMap);
|
||||
FxEngine fxEngine(NUM_LEDS);
|
||||
UICheckbox switchFx("Switch Fx", true);
|
||||
|
||||
void setup() {
|
||||
delay(1000); // sanity delay
|
||||
FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS)
|
||||
.setCorrection(TypicalLEDStrip)
|
||||
.setScreenMap(MATRIX_WIDTH, MATRIX_HEIGHT);
|
||||
FastLED.setBrightness(96);
|
||||
noisePalette1.setPalettePreset(2);
|
||||
noisePalette2.setPalettePreset(4);
|
||||
fxEngine.addFx(noisePalette1);
|
||||
fxEngine.addFx(noisePalette2);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
noisePalette1.setSpeed(SPEED);
|
||||
noisePalette1.setScale(SCALE);
|
||||
noisePalette2.setSpeed(SPEED);
|
||||
noisePalette2.setScale(int(SCALE) * 3 / 2); // Make the different.
|
||||
EVERY_N_SECONDS(1) {
|
||||
if (switchFx) {
|
||||
fxEngine.nextFx(500);
|
||||
}
|
||||
}
|
||||
fxEngine.draw(millis(), leds);
|
||||
FastLED.show();
|
||||
}
|
||||
|
||||
#endif // __AVR__
|
||||
@@ -0,0 +1,83 @@
|
||||
/// @file FxFire2012.ino
|
||||
/// @brief Fire2012 effect with ScreenMap
|
||||
/// @example FxFire2012.ino
|
||||
///
|
||||
/// 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`
|
||||
/// 4. When the compiler is done a web page will open.
|
||||
|
||||
/// @brief Simple one-dimensional fire animation function
|
||||
// Fire2012 by Mark Kriegsman, July 2012
|
||||
// as part of "Five Elements" shown here: http://youtu.be/knWiGsmgycY
|
||||
////
|
||||
// This basic one-dimensional 'fire' simulation works roughly as follows:
|
||||
// There's a underlying array of 'heat' cells, that model the temperature
|
||||
// at each point along the line. Every cycle through the simulation,
|
||||
// four steps are performed:
|
||||
// 1) All cells cool down a little bit, losing heat to the air
|
||||
// 2) The heat from each cell drifts 'up' and diffuses a little
|
||||
// 3) Sometimes randomly new 'sparks' of heat are added at the bottom
|
||||
// 4) The heat from each cell is rendered as a color into the leds array
|
||||
// The heat-to-color mapping uses a black-body radiation approximation.
|
||||
//
|
||||
// Temperature is in arbitrary units from 0 (cold black) to 255 (white hot).
|
||||
//
|
||||
// This simulation scales it self a bit depending on NUM_LEDS; it should look
|
||||
// "OK" on anywhere from 20 to 100 LEDs without too much tweaking.
|
||||
//
|
||||
// I recommend running this simulation at anywhere from 30-100 frames per second,
|
||||
// meaning an interframe delay of about 10-35 milliseconds.
|
||||
//
|
||||
// Looks best on a high-density LED setup (60+ pixels/meter).
|
||||
//
|
||||
//
|
||||
// There are two main parameters you can play with to control the look and
|
||||
// feel of your fire: COOLING (used in step 1 above), and SPARKING (used
|
||||
// in step 3 above).
|
||||
//
|
||||
// COOLING: How much does the air cool as it rises?
|
||||
// Less cooling = taller flames. More cooling = shorter flames.
|
||||
// Default 50, suggested range 20-100
|
||||
|
||||
// SPARKING: What chance (out of 255) is there that a new spark will be lit?
|
||||
// Higher chance = more roaring fire. Lower chance = more flickery fire.
|
||||
// Default 120, suggested range 50-200.
|
||||
|
||||
#include <FastLED.h>
|
||||
#include "fx/1d/fire2012.h"
|
||||
#include "fl/screenmap.h"
|
||||
|
||||
using namespace fl;
|
||||
|
||||
#define LED_PIN 5
|
||||
#define COLOR_ORDER GRB
|
||||
#define CHIPSET WS2811
|
||||
#define NUM_LEDS 92
|
||||
|
||||
#define BRIGHTNESS 128
|
||||
#define FRAMES_PER_SECOND 30
|
||||
#define COOLING 55
|
||||
#define SPARKING 120
|
||||
#define REVERSE_DIRECTION false
|
||||
|
||||
CRGB leds[NUM_LEDS];
|
||||
Fire2012Ptr fire = fl::make_shared<Fire2012>(NUM_LEDS, COOLING, SPARKING, REVERSE_DIRECTION);
|
||||
|
||||
void setup() {
|
||||
ScreenMap screenMap = ScreenMap::DefaultStrip(NUM_LEDS, 1.5, .4);
|
||||
FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS)
|
||||
.setCorrection(TypicalLEDStrip)
|
||||
.setScreenMap(screenMap)
|
||||
.setRgbw();
|
||||
FastLED.setBrightness(BRIGHTNESS);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
fire->draw(Fx::DrawContext(millis(), leds)); // run simulation frame
|
||||
|
||||
FastLED.show(millis()); // display this frame
|
||||
FastLED.delay(1000 / FRAMES_PER_SECOND);
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/// @file FxGfx2Video.ino
|
||||
/// @brief Demonstrates graphics to video conversion
|
||||
/// @example FxGfx2Video.ino
|
||||
///
|
||||
/// 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`
|
||||
/// 4. When the compiler is done a web page will open.
|
||||
|
||||
/// @file Gfx2Video.ino
|
||||
/// @brief Demonstrates drawing to a frame buffer, which is used as source video for the memory player.
|
||||
/// The render pattern is alternating black/red pixels as a checkerboard.
|
||||
/// @example VideoTest.ino
|
||||
|
||||
|
||||
#ifndef COMPILE_VIDEO_STREAM
|
||||
#if defined(__AVR__)
|
||||
// This has grown too large for the AVR to handle.
|
||||
#define COMPILE_VIDEO_STREAM 0
|
||||
#else
|
||||
#define COMPILE_VIDEO_STREAM 1
|
||||
#endif
|
||||
#endif // COMPILE_VIDEO_STREAM
|
||||
|
||||
#if COMPILE_VIDEO_STREAM
|
||||
|
||||
|
||||
#include <FastLED.h>
|
||||
#include "fl/bytestreammemory.h"
|
||||
#include "fx/fx_engine.h"
|
||||
#include "fl/memory.h"
|
||||
#include "fx/video.h"
|
||||
#include "fl/dbg.h"
|
||||
|
||||
using namespace fl;
|
||||
|
||||
#define LED_PIN 2
|
||||
#define BRIGHTNESS 96
|
||||
#define LED_TYPE WS2811
|
||||
#define COLOR_ORDER GRB
|
||||
|
||||
#define MATRIX_WIDTH 22
|
||||
#define MATRIX_HEIGHT 22
|
||||
#define NUM_LEDS (MATRIX_WIDTH * MATRIX_HEIGHT)
|
||||
|
||||
CRGB leds[NUM_LEDS];
|
||||
|
||||
const int BYTES_PER_FRAME = 3 * NUM_LEDS;
|
||||
const int NUM_FRAMES = 2;
|
||||
const uint32_t BUFFER_SIZE = BYTES_PER_FRAME * NUM_FRAMES;
|
||||
|
||||
ByteStreamMemoryPtr memoryStream;
|
||||
FxEngine fxEngine(NUM_LEDS);
|
||||
// Create and initialize Video object
|
||||
XYMap xymap(MATRIX_WIDTH, MATRIX_HEIGHT);
|
||||
Video video(NUM_LEDS, 2.0f);
|
||||
|
||||
void write_one_frame(ByteStreamMemoryPtr memoryStream) {
|
||||
//memoryStream->seek(0); // Reset to the beginning of the stream
|
||||
uint32_t total_bytes_written = 0;
|
||||
bool toggle = (millis() / 500) % 2 == 0;
|
||||
FASTLED_DBG("Writing frame data, toggle = " << toggle);
|
||||
for (uint32_t i = 0; i < NUM_LEDS; ++i) {
|
||||
CRGB color = (toggle ^ i%2) ? CRGB::Black : CRGB::Red;
|
||||
size_t bytes_written = memoryStream->writeCRGB(&color, 1);
|
||||
if (bytes_written != 1) {
|
||||
FASTLED_DBG("Failed to write frame data, wrote " << bytes_written << " bytes");
|
||||
break;
|
||||
}
|
||||
total_bytes_written += bytes_written;
|
||||
}
|
||||
if (total_bytes_written) {
|
||||
FASTLED_DBG("Frame written, total bytes: " << total_bytes_written);
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
delay(1000); // sanity delay
|
||||
FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS)
|
||||
.setCorrection(TypicalLEDStrip)
|
||||
.setScreenMap(xymap);
|
||||
FastLED.setBrightness(BRIGHTNESS);
|
||||
|
||||
// Create and fill the ByteStreamMemory with test data
|
||||
memoryStream = fl::make_shared<ByteStreamMemory>(BUFFER_SIZE*sizeof(CRGB));
|
||||
|
||||
video.beginStream(memoryStream);
|
||||
// Add the video effect to the FxEngine
|
||||
fxEngine.addFx(video);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
EVERY_N_MILLISECONDS(500) {
|
||||
write_one_frame(memoryStream); // Write next frame data
|
||||
}
|
||||
// write_one_frame(memoryStream); // Write next frame data
|
||||
// Draw the frame
|
||||
fxEngine.draw(millis(), leds);
|
||||
// Show the LEDs
|
||||
FastLED.show();
|
||||
delay(20); // Adjust this delay to control frame rate
|
||||
}
|
||||
#else
|
||||
void setup() {}
|
||||
void loop() {}
|
||||
#endif // COMPILE_VIDEO_STREAM
|
||||
@@ -0,0 +1,101 @@
|
||||
/// @file FxNoisePlusPalette.ino
|
||||
/// @brief Noise plus palette effect with XYMap
|
||||
/// @example FxNoisePlusPalette.ino
|
||||
///
|
||||
/// 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`
|
||||
/// 4. When the compiler is done a web page will open.
|
||||
|
||||
#include <FastLED.h>
|
||||
|
||||
#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 "fx/2d/noisepalette.h"
|
||||
#include "fl/ui.h"
|
||||
|
||||
using namespace fl;
|
||||
|
||||
#define LED_PIN 3
|
||||
#define BRIGHTNESS 96
|
||||
#define LED_TYPE WS2811
|
||||
#define COLOR_ORDER GRB
|
||||
|
||||
#define MATRIX_WIDTH 16
|
||||
#define MATRIX_HEIGHT 16
|
||||
|
||||
#if __EMSCRIPTEN__
|
||||
#define GRID_SERPENTINE 0
|
||||
#else
|
||||
#define GRID_SERPENTINE 1
|
||||
#endif
|
||||
|
||||
#define NUM_LEDS (MATRIX_WIDTH * MATRIX_HEIGHT)
|
||||
|
||||
// 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 proceedurally generated
|
||||
// palettes.
|
||||
|
||||
// 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 iwll be. A
|
||||
// value of 1 will be so zoomed in, you'll mostly see solid colors.
|
||||
|
||||
UISlider SCALE("SCALE", 20, 1, 100, 1);
|
||||
|
||||
// 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.
|
||||
UISlider SPEED("SPEED", 30, 1, 60, 1);
|
||||
|
||||
CRGB leds[NUM_LEDS];
|
||||
XYMap xyMap(MATRIX_WIDTH, MATRIX_HEIGHT, GRID_SERPENTINE);
|
||||
NoisePalette noisePalette(xyMap);
|
||||
|
||||
void setup() {
|
||||
delay(1000); // sanity delay
|
||||
FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS)
|
||||
.setCorrection(TypicalLEDStrip);
|
||||
FastLED.setBrightness(96);
|
||||
noisePalette.setSpeed(SPEED);
|
||||
noisePalette.setScale(SCALE);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
noisePalette.setSpeed(SPEED);
|
||||
noisePalette.setScale(SCALE);
|
||||
EVERY_N_MILLISECONDS(5000) { noisePalette.changeToRandomPalette(); }
|
||||
|
||||
noisePalette.draw(Fx::DrawContext(millis(), leds));
|
||||
FastLED.show();
|
||||
}
|
||||
|
||||
#endif // End of the non-AVR code section
|
||||
682
.pio/libdeps/esp01_1m/FastLED/examples/FxNoiseRing/FxNoiseRing.h
Normal file
682
.pio/libdeps/esp01_1m/FastLED/examples/FxNoiseRing/FxNoiseRing.h
Normal file
@@ -0,0 +1,682 @@
|
||||
/// @file FxNoiseRing.ino
|
||||
/// @brief Noise effect on circular ring with ScreenMap
|
||||
/// @example FxNoiseRing.ino
|
||||
///
|
||||
/// 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`
|
||||
/// 4. When the compiler is done a web page will open.
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <FastLED.h>
|
||||
|
||||
|
||||
#include "fl/json.h"
|
||||
#include "fl/math_macros.h"
|
||||
#include "fl/warn.h"
|
||||
#include "noisegen.h"
|
||||
#include "fl/screenmap.h"
|
||||
#include "fl/slice.h"
|
||||
#include "fl/ui.h"
|
||||
|
||||
#include "sensors/pir.h"
|
||||
#include "./simple_timer.h"
|
||||
#include "fl/sstream.h"
|
||||
#include "fl/assert.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define LED_PIN 2
|
||||
#define COLOR_ORDER GRB // Color order matters for a real device, web-compiler will ignore this.
|
||||
#define NUM_LEDS 250
|
||||
#define PIN_PIR 0
|
||||
|
||||
#define PIR_LATCH_MS 60000 // how long to keep the PIR sensor active after a trigger
|
||||
#define PIR_RISING_TIME 1000 // how long to fade in the PIR sensor
|
||||
#define PIR_FALLING_TIME 1000 // how long to fade out the PIR sensor
|
||||
|
||||
using namespace fl;
|
||||
|
||||
CRGB leds[NUM_LEDS];
|
||||
|
||||
// Enhanced coordinate system for ring-based effects
|
||||
struct RingCoord {
|
||||
float angle; // Position on ring (0 to 2π)
|
||||
float radius; // Distance from center (normalized 0-1)
|
||||
float x, y; // Cartesian coordinates
|
||||
int led_index; // LED position on strip
|
||||
};
|
||||
|
||||
// Convert LED index to ring coordinates
|
||||
RingCoord calculateRingCoord(int led_index, int num_leds, float time_offset = 0.0f) {
|
||||
RingCoord coord;
|
||||
coord.led_index = led_index;
|
||||
coord.angle = (led_index * 2.0f * M_PI / num_leds) + time_offset;
|
||||
coord.radius = 1.0f; // Fixed radius for ring
|
||||
coord.x = cos(coord.angle);
|
||||
coord.y = sin(coord.angle);
|
||||
return coord;
|
||||
}
|
||||
|
||||
// Performance optimization with lookup tables
|
||||
class RingLUT {
|
||||
private:
|
||||
float cos_table[NUM_LEDS];
|
||||
float sin_table[NUM_LEDS];
|
||||
|
||||
public:
|
||||
void initialize() {
|
||||
for(int i = 0; i < NUM_LEDS; i++) {
|
||||
float angle = i * 2.0f * M_PI / NUM_LEDS;
|
||||
cos_table[i] = cos(angle);
|
||||
sin_table[i] = sin(angle);
|
||||
}
|
||||
}
|
||||
|
||||
RingCoord fastRingCoord(int led_index, float time_offset = 0.0f) {
|
||||
RingCoord coord;
|
||||
coord.led_index = led_index;
|
||||
coord.angle = (led_index * 2.0f * M_PI / NUM_LEDS) + time_offset;
|
||||
coord.x = cos_table[led_index];
|
||||
coord.y = sin_table[led_index];
|
||||
coord.radius = 1.0f;
|
||||
return coord;
|
||||
}
|
||||
};
|
||||
|
||||
// Plasma wave parameters
|
||||
struct PlasmaParams {
|
||||
float time_scale = 1.0f;
|
||||
float noise_intensity = 0.5f;
|
||||
float noise_amplitude = 0.8f;
|
||||
uint8_t time_bitshift = 5;
|
||||
uint8_t hue_offset = 0;
|
||||
float brightness = 1.0f;
|
||||
};
|
||||
|
||||
// Plasma wave generator - Featured Implementation
|
||||
class PlasmaWaveGenerator {
|
||||
private:
|
||||
struct WaveSource {
|
||||
float x, y; // Source position
|
||||
float frequency; // Wave frequency
|
||||
float amplitude; // Wave strength
|
||||
float phase_speed; // Phase evolution rate
|
||||
};
|
||||
|
||||
WaveSource sources[4] = {
|
||||
{0.5f, 0.5f, 1.0f, 1.0f, 0.8f}, // Center source
|
||||
{0.0f, 0.0f, 1.5f, 0.8f, 1.2f}, // Corner source
|
||||
{1.0f, 1.0f, 0.8f, 1.2f, 0.6f}, // Opposite corner
|
||||
{0.5f, 0.0f, 1.2f, 0.9f, 1.0f} // Edge source
|
||||
};
|
||||
|
||||
public:
|
||||
CRGB calculatePlasmaPixel(const RingCoord& coord, uint32_t time_ms, const PlasmaParams& params) {
|
||||
float time_scaled = time_ms * params.time_scale * 0.001f;
|
||||
|
||||
// Calculate wave interference
|
||||
float wave_sum = 0.0f;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
float dx = coord.x - sources[i].x;
|
||||
float dy = coord.y - sources[i].y;
|
||||
float distance = sqrt(dx*dx + dy*dy);
|
||||
|
||||
float wave_phase = distance * sources[i].frequency + time_scaled * sources[i].phase_speed;
|
||||
wave_sum += sin(wave_phase) * sources[i].amplitude;
|
||||
}
|
||||
|
||||
// Add noise modulation for organic feel
|
||||
float noise_scale = params.noise_intensity;
|
||||
float noise_x = coord.x * 0xffff * noise_scale;
|
||||
float noise_y = coord.y * 0xffff * noise_scale;
|
||||
uint32_t noise_time = time_ms << params.time_bitshift;
|
||||
|
||||
float noise_mod = (inoise16(noise_x, noise_y, noise_time) - 32768) / 65536.0f;
|
||||
wave_sum += noise_mod * params.noise_amplitude;
|
||||
|
||||
// Map to color space
|
||||
return mapWaveToColor(wave_sum, params);
|
||||
}
|
||||
|
||||
private:
|
||||
CRGB mapWaveToColor(float wave_value, const PlasmaParams& params) {
|
||||
// Normalize wave to 0-1 range
|
||||
float normalized = (wave_value + 4.0f) / 8.0f; // Assuming max amplitude ~4
|
||||
normalized = constrain(normalized, 0.0f, 1.0f);
|
||||
|
||||
// Create flowing hue based on wave phase
|
||||
uint8_t hue = (uint8_t)(normalized * 255.0f + params.hue_offset) % 256;
|
||||
|
||||
// Dynamic saturation based on wave intensity
|
||||
float intensity = abs(wave_value);
|
||||
uint8_t sat = (uint8_t)(192 + intensity * 63); // High saturation with variation
|
||||
|
||||
// Brightness modulation
|
||||
uint8_t val = (uint8_t)(normalized * 255.0f * params.brightness);
|
||||
|
||||
return CHSV(hue, sat, val);
|
||||
}
|
||||
};
|
||||
|
||||
// Advanced Color Palette System
|
||||
class ColorPaletteManager {
|
||||
private:
|
||||
uint8_t current_palette = 0;
|
||||
uint32_t last_palette_change = 0;
|
||||
static const uint32_t PALETTE_CHANGE_INTERVAL = 5000; // 5 seconds
|
||||
|
||||
public:
|
||||
void update(uint32_t now, bool auto_cycle_enabled, uint8_t manual_palette) {
|
||||
if (auto_cycle_enabled) {
|
||||
if (now - last_palette_change > PALETTE_CHANGE_INTERVAL) {
|
||||
current_palette = (current_palette + 1) % 5;
|
||||
last_palette_change = now;
|
||||
}
|
||||
} else {
|
||||
current_palette = manual_palette;
|
||||
}
|
||||
}
|
||||
|
||||
CRGB mapColor(float hue_norm, float intensity, float special_param = 0.0f) {
|
||||
switch(current_palette) {
|
||||
case 0: return mapSunsetBoulevard(hue_norm, intensity, special_param);
|
||||
case 1: return mapOceanBreeze(hue_norm, intensity, special_param);
|
||||
case 2: return mapNeonNights(hue_norm, intensity, special_param);
|
||||
case 3: return mapForestWhisper(hue_norm, intensity, special_param);
|
||||
case 4: return mapGalaxyExpress(hue_norm, intensity, special_param);
|
||||
default: return mapSunsetBoulevard(hue_norm, intensity, special_param);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
CRGB mapSunsetBoulevard(float hue_norm, float intensity, float special_param) {
|
||||
// Warm oranges, deep reds, golden yellows (Hue 0-45)
|
||||
uint8_t hue = (uint8_t)(hue_norm * 45);
|
||||
uint8_t sat = 200 + (uint8_t)(intensity * 55);
|
||||
uint8_t val = 150 + (uint8_t)(intensity * 105);
|
||||
return CHSV(hue, sat, val);
|
||||
}
|
||||
|
||||
CRGB mapOceanBreeze(float hue_norm, float intensity, float special_param) {
|
||||
// Deep blues, aqua, seafoam green (Hue 120-210)
|
||||
uint8_t hue = 120 + (uint8_t)(hue_norm * 90);
|
||||
uint8_t sat = 180 + (uint8_t)(intensity * 75);
|
||||
uint8_t val = 120 + (uint8_t)(intensity * 135);
|
||||
return CHSV(hue, sat, val);
|
||||
}
|
||||
|
||||
CRGB mapNeonNights(float hue_norm, float intensity, float special_param) {
|
||||
// Electric pink, cyan, purple, lime green - high contrast
|
||||
uint8_t base_hues[] = {0, 85, 128, 192}; // Red, Cyan, Pink, Purple
|
||||
uint8_t selected_hue = base_hues[(int)(hue_norm * 4) % 4];
|
||||
uint8_t sat = 255; // Maximum saturation for neon effect
|
||||
uint8_t val = 100 + (uint8_t)(intensity * 155);
|
||||
return CHSV(selected_hue, sat, val);
|
||||
}
|
||||
|
||||
CRGB mapForestWhisper(float hue_norm, float intensity, float special_param) {
|
||||
// Deep greens, earth browns, golden highlights (Hue 60-150)
|
||||
uint8_t hue = 60 + (uint8_t)(hue_norm * 90);
|
||||
uint8_t sat = 150 + (uint8_t)(intensity * 105);
|
||||
uint8_t val = 100 + (uint8_t)(intensity * 155);
|
||||
return CHSV(hue, sat, val);
|
||||
}
|
||||
|
||||
CRGB mapGalaxyExpress(float hue_norm, float intensity, float special_param) {
|
||||
// Deep purples, cosmic blues, silver stars (Hue 200-300)
|
||||
if (special_param > 0.8f) {
|
||||
// Silver/white stars
|
||||
uint8_t brightness = 200 + (uint8_t)(intensity * 55);
|
||||
return CRGB(brightness, brightness, brightness);
|
||||
} else {
|
||||
uint8_t hue = 200 + (uint8_t)(hue_norm * 100);
|
||||
uint8_t sat = 180 + (uint8_t)(intensity * 75);
|
||||
uint8_t val = 80 + (uint8_t)(intensity * 175);
|
||||
return CHSV(hue, sat, val);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ALL 10 ALGORITHM IMPLEMENTATIONS
|
||||
CRGB drawCosmicSwirl(const RingCoord& coord, uint32_t time_ms, ColorPaletteManager& palette) {
|
||||
float time_factor = time_ms * 0.0008f;
|
||||
|
||||
// Multi-octave noise for organic complexity
|
||||
float noise1 = inoise16(coord.x * 2000, coord.y * 2000, time_factor * 1000) / 65536.0f;
|
||||
float noise2 = inoise16(coord.x * 1000, coord.y * 1000, time_factor * 2000) / 65536.0f * 0.5f;
|
||||
float noise3 = inoise16(coord.x * 4000, coord.y * 4000, time_factor * 500) / 65536.0f * 0.25f;
|
||||
|
||||
float combined_noise = noise1 + noise2 + noise3;
|
||||
float hue_norm = (combined_noise + coord.angle / (2*M_PI) + 1.0f) * 0.5f;
|
||||
float intensity = (combined_noise + 1.0f) * 0.5f;
|
||||
|
||||
return palette.mapColor(hue_norm, intensity);
|
||||
}
|
||||
|
||||
CRGB drawElectricStorm(const RingCoord& coord, uint32_t time_ms, ColorPaletteManager& palette) {
|
||||
uint32_t fast_time = time_ms << 3; // 8x time acceleration
|
||||
|
||||
float x_noise = coord.x * 8000;
|
||||
float y_noise = coord.y * 8000;
|
||||
|
||||
uint16_t noise1 = inoise16(x_noise, y_noise, fast_time);
|
||||
uint16_t noise2 = inoise16(x_noise + 10000, y_noise + 10000, fast_time + 5000);
|
||||
|
||||
uint8_t threshold = 200;
|
||||
bool lightning = (noise1 >> 8) > threshold || (noise2 >> 8) > threshold;
|
||||
|
||||
if (lightning) {
|
||||
float lightning_intensity = max((noise1 >> 8) - threshold, (noise2 >> 8) - threshold) / 55.0f;
|
||||
return palette.mapColor(0.7f, lightning_intensity, 1.0f); // Special lightning effect
|
||||
} else {
|
||||
float storm_intensity = (noise1 >> 8) / 1020.0f; // Very low intensity for background
|
||||
return palette.mapColor(0.6f, storm_intensity);
|
||||
}
|
||||
}
|
||||
|
||||
CRGB drawLavaLamp(const RingCoord& coord, uint32_t time_ms, ColorPaletteManager& palette) {
|
||||
float slow_time = time_ms * 0.0002f;
|
||||
|
||||
float blob_scale = 800;
|
||||
uint16_t primary_noise = inoise16(coord.x * blob_scale, coord.y * blob_scale, slow_time * 1000);
|
||||
uint16_t secondary_noise = inoise16(coord.x * blob_scale * 0.5f, coord.y * blob_scale * 0.5f, slow_time * 1500);
|
||||
|
||||
float blob_value = (primary_noise + secondary_noise * 0.3f) / 65536.0f;
|
||||
|
||||
if (blob_value > 0.6f) {
|
||||
// Hot blob center
|
||||
float intensity = (blob_value - 0.6f) / 0.4f;
|
||||
return palette.mapColor(0.1f, intensity); // Warm colors
|
||||
} else if (blob_value > 0.3f) {
|
||||
// Blob edge gradient
|
||||
float edge_factor = (blob_value - 0.3f) / 0.3f;
|
||||
return palette.mapColor(0.2f, edge_factor);
|
||||
} else {
|
||||
// Background
|
||||
return palette.mapColor(0.8f, 0.2f); // Cool background
|
||||
}
|
||||
}
|
||||
|
||||
CRGB drawDigitalRain(const RingCoord& coord, uint32_t time_ms, ColorPaletteManager& palette) {
|
||||
float vertical_pos = sin(coord.angle) * 0.5f + 0.5f;
|
||||
float cascade_speed = 0.002f;
|
||||
float time_offset = time_ms * cascade_speed;
|
||||
|
||||
int stream_id = (int)(coord.angle * 10) % 8;
|
||||
float stream_phase = fmod(vertical_pos + time_offset + stream_id * 0.125f, 1.0f);
|
||||
|
||||
uint16_t noise = inoise16(stream_id * 1000, stream_phase * 10000, time_ms / 4);
|
||||
uint8_t digital_value = (noise >> 8) > 128 ? 255 : 0;
|
||||
|
||||
if (digital_value > 0) {
|
||||
float intensity = 1.0f - stream_phase * 0.8f; // Fade trailing
|
||||
return palette.mapColor(0.4f, intensity); // Matrix green area
|
||||
} else {
|
||||
return CRGB::Black;
|
||||
}
|
||||
}
|
||||
|
||||
CRGB drawGlitchCity(const RingCoord& coord, uint32_t time_ms, ColorPaletteManager& palette) {
|
||||
uint32_t glitch_time = (time_ms / 100) * 100; // Quantize time
|
||||
|
||||
uint16_t noise1 = inoise16(coord.x * 3000, coord.y * 3000, glitch_time);
|
||||
uint16_t noise2 = inoise16(coord.x * 5000, coord.y * 5000, glitch_time + 1000);
|
||||
|
||||
uint16_t glitch_value = noise1 ^ noise2; // XOR for harsh digital effects
|
||||
|
||||
if ((glitch_value & 0xF000) == 0xF000) {
|
||||
return CRGB(255, 255, 255); // Full-bright glitch flash
|
||||
}
|
||||
|
||||
float intensity = (glitch_value & 0xFF) / 255.0f;
|
||||
float hue_chaos = ((glitch_value >> 8) & 0xFF) / 255.0f;
|
||||
|
||||
return palette.mapColor(hue_chaos, intensity, 0.5f);
|
||||
}
|
||||
|
||||
CRGB drawOceanDepths(const RingCoord& coord, uint32_t time_ms, ColorPaletteManager& palette) {
|
||||
float ocean_time = time_ms * 0.0005f;
|
||||
|
||||
float current1 = inoise16(coord.x * 1200, coord.y * 1200, ocean_time * 800) / 65536.0f;
|
||||
float current2 = inoise16(coord.x * 2400, coord.y * 2400, ocean_time * 600) / 65536.0f * 0.5f;
|
||||
float current3 = inoise16(coord.x * 600, coord.y * 600, ocean_time * 1000) / 65536.0f * 0.3f;
|
||||
|
||||
float depth_factor = (current1 + current2 + current3 + 1.5f) / 3.0f;
|
||||
float hue_variation = (current2 + 0.5f);
|
||||
|
||||
return palette.mapColor(hue_variation, depth_factor);
|
||||
}
|
||||
|
||||
CRGB drawFireDance(const RingCoord& coord, uint32_t time_ms, ColorPaletteManager& palette) {
|
||||
float vertical_component = sin(coord.angle) * 0.5f + 0.5f;
|
||||
|
||||
float flame_x = coord.x * 1500;
|
||||
float flame_y = coord.y * 1500 + time_ms * 0.003f;
|
||||
|
||||
uint16_t turbulence = inoise16(flame_x, flame_y, time_ms);
|
||||
float flame_intensity = (turbulence / 65536.0f) * (1.0f - vertical_component * 0.3f);
|
||||
|
||||
float fire_hue = flame_intensity * 0.15f; // Red to orange range
|
||||
return palette.mapColor(fire_hue, flame_intensity);
|
||||
}
|
||||
|
||||
CRGB drawNebulaDrift(const RingCoord& coord, uint32_t time_ms, ColorPaletteManager& palette) {
|
||||
float nebula_time = time_ms * 0.0003f;
|
||||
|
||||
float cloud1 = inoise16(coord.x * 800, coord.y * 800, nebula_time * 1000) / 65536.0f;
|
||||
float cloud2 = inoise16(coord.x * 1600, coord.y * 1600, nebula_time * 700) / 65536.0f * 0.5f;
|
||||
float cloud3 = inoise16(coord.x * 400, coord.y * 400, nebula_time * 1200) / 65536.0f * 0.25f;
|
||||
|
||||
float nebula_density = cloud1 + cloud2 + cloud3;
|
||||
|
||||
uint16_t star_noise = inoise16(coord.x * 4000, coord.y * 4000, nebula_time * 200);
|
||||
bool is_star = (star_noise > 60000);
|
||||
|
||||
if (is_star) {
|
||||
float star_intensity = (star_noise - 60000) / 5536.0f;
|
||||
return palette.mapColor(0.0f, star_intensity, 1.0f); // Stars
|
||||
} else {
|
||||
float hue_drift = (nebula_density + 1.0f) * 0.5f;
|
||||
float intensity = (nebula_density + 1.0f) * 0.4f;
|
||||
return palette.mapColor(hue_drift, intensity);
|
||||
}
|
||||
}
|
||||
|
||||
CRGB drawBinaryPulse(const RingCoord& coord, uint32_t time_ms, ColorPaletteManager& palette) {
|
||||
float pulse_period = 2000.0f;
|
||||
float pulse_phase = fmod(time_ms, pulse_period) / pulse_period;
|
||||
|
||||
float distance_from_center = sqrt(coord.x * coord.x + coord.y * coord.y);
|
||||
|
||||
float ring_frequency = 5.0f;
|
||||
float pulse_offset = pulse_phase * 2.0f;
|
||||
float ring_value = sin((distance_from_center * ring_frequency - pulse_offset) * 2 * M_PI);
|
||||
|
||||
uint16_t noise = inoise16(coord.x * 2000, coord.y * 2000, time_ms / 8);
|
||||
float digital_mod = ((noise >> 8) > 128) ? 1.0f : -0.5f;
|
||||
|
||||
float final_value = ring_value * digital_mod;
|
||||
|
||||
if (final_value > 0.3f) {
|
||||
return palette.mapColor(0.8f, final_value, 0.8f); // Active pulse
|
||||
} else if (final_value > -0.2f) {
|
||||
float transition_intensity = (final_value + 0.2f) * 2.0f;
|
||||
return palette.mapColor(0.3f, transition_intensity); // Transition zones
|
||||
} else {
|
||||
return palette.mapColor(0.7f, 0.1f); // Background
|
||||
}
|
||||
}
|
||||
|
||||
// Enhanced variant manager with ALL 10 ALGORITHMS and smooth transitions
|
||||
class NoiseVariantManager {
|
||||
private:
|
||||
uint8_t current_variant = 0;
|
||||
uint8_t target_variant = 0;
|
||||
float transition_progress = 1.0f; // 0.0 = old, 1.0 = new
|
||||
uint32_t transition_start = 0;
|
||||
static const uint32_t TRANSITION_DURATION = 1500; // 1.5 second fade for smoother transitions
|
||||
|
||||
// Algorithm instances
|
||||
PlasmaWaveGenerator plasma_gen;
|
||||
PlasmaParams plasma_params;
|
||||
ColorPaletteManager& palette_manager;
|
||||
|
||||
public:
|
||||
NoiseVariantManager(ColorPaletteManager& palette_mgr) : palette_manager(palette_mgr) {}
|
||||
|
||||
void update(uint32_t now, bool auto_cycle_enabled, uint8_t manual_variant, const PlasmaParams& params) {
|
||||
plasma_params = params;
|
||||
|
||||
// Handle automatic cycling vs manual override
|
||||
if (auto_cycle_enabled) {
|
||||
EVERY_N_MILLISECONDS(12000) { // Slightly longer for each variant
|
||||
startTransition((current_variant + 1) % 10, now); // ALL 10 variants
|
||||
}
|
||||
} else if (manual_variant != target_variant && transition_progress >= 1.0f) {
|
||||
// Manual override
|
||||
startTransition(manual_variant, now);
|
||||
}
|
||||
|
||||
// Update transition progress
|
||||
if (transition_progress < 1.0f) {
|
||||
uint32_t elapsed = now - transition_start;
|
||||
transition_progress = min(1.0f, elapsed / (float)TRANSITION_DURATION);
|
||||
|
||||
if (transition_progress >= 1.0f) {
|
||||
current_variant = target_variant;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CRGB renderPixel(const RingCoord& coord, uint32_t time_ms) {
|
||||
if (transition_progress >= 1.0f) {
|
||||
// No transition, render current variant
|
||||
return renderVariant(current_variant, coord, time_ms);
|
||||
} else {
|
||||
// Advanced cross-fade with brightness preservation
|
||||
CRGB old_color = renderVariant(current_variant, coord, time_ms);
|
||||
CRGB new_color = renderVariant(target_variant, coord, time_ms);
|
||||
return smoothLerpCRGB(old_color, new_color, transition_progress);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t getCurrentVariant() const { return current_variant; }
|
||||
const char* getCurrentVariantName() const {
|
||||
const char* names[] = {
|
||||
"Cosmic Swirl", "Electric Storm", "Lava Lamp", "Digital Rain", "Plasma Waves",
|
||||
"Glitch City", "Ocean Depths", "Fire Dance", "Nebula Drift", "Binary Pulse"
|
||||
};
|
||||
return names[current_variant % 10];
|
||||
}
|
||||
|
||||
private:
|
||||
void startTransition(uint8_t new_variant, uint32_t now) {
|
||||
target_variant = new_variant % 10; // Ensure valid range
|
||||
transition_start = now;
|
||||
transition_progress = 0.0f;
|
||||
}
|
||||
|
||||
CRGB renderVariant(uint8_t variant, const RingCoord& coord, uint32_t time_ms) {
|
||||
switch(variant % 10) {
|
||||
case 0: return drawCosmicSwirl(coord, time_ms, palette_manager);
|
||||
case 1: return drawElectricStorm(coord, time_ms, palette_manager);
|
||||
case 2: return drawLavaLamp(coord, time_ms, palette_manager);
|
||||
case 3: return drawDigitalRain(coord, time_ms, palette_manager);
|
||||
case 4: return drawPlasmaWithPalette(coord, time_ms, palette_manager);
|
||||
case 5: return drawGlitchCity(coord, time_ms, palette_manager);
|
||||
case 6: return drawOceanDepths(coord, time_ms, palette_manager);
|
||||
case 7: return drawFireDance(coord, time_ms, palette_manager);
|
||||
case 8: return drawNebulaDrift(coord, time_ms, palette_manager);
|
||||
case 9: return drawBinaryPulse(coord, time_ms, palette_manager);
|
||||
default: return drawCosmicSwirl(coord, time_ms, palette_manager);
|
||||
}
|
||||
}
|
||||
|
||||
// Enhanced Plasma Waves with palette integration
|
||||
CRGB drawPlasmaWithPalette(const RingCoord& coord, uint32_t time_ms, ColorPaletteManager& palette) {
|
||||
// Generate base plasma waves
|
||||
CRGB plasma_color = plasma_gen.calculatePlasmaPixel(coord, time_ms, plasma_params);
|
||||
|
||||
// Extract intensity and hue information from plasma
|
||||
float intensity = (plasma_color.r + plasma_color.g + plasma_color.b) / 765.0f;
|
||||
|
||||
// Calculate wave interference for hue mapping
|
||||
float time_scaled = time_ms * plasma_params.time_scale * 0.001f;
|
||||
float wave_sum = 0.0f;
|
||||
|
||||
// Simplified wave calculation for hue determination
|
||||
float dx = coord.x - 0.5f;
|
||||
float dy = coord.y - 0.5f;
|
||||
float distance = sqrt(dx*dx + dy*dy);
|
||||
float wave_phase = distance * 2.0f + time_scaled * 1.5f;
|
||||
wave_sum = sin(wave_phase);
|
||||
|
||||
float hue_norm = (wave_sum + 1.0f) * 0.5f; // Normalize to 0-1
|
||||
|
||||
// Use palette system for consistent color theming
|
||||
return palette.mapColor(hue_norm, intensity, intensity > 0.8f ? 1.0f : 0.0f);
|
||||
}
|
||||
|
||||
// Enhanced interpolation with brightness preservation and smooth curves
|
||||
CRGB smoothLerpCRGB(const CRGB& a, const CRGB& b, float t) {
|
||||
// Apply smooth curve to transition
|
||||
float smooth_t = t * t * (3.0f - 2.0f * t); // Smoothstep function
|
||||
|
||||
// Preserve brightness during transition to avoid flickering
|
||||
float brightness_a = (a.r + a.g + a.b) / 765.0f;
|
||||
float brightness_b = (b.r + b.g + b.b) / 765.0f;
|
||||
float target_brightness = brightness_a + (brightness_b - brightness_a) * smooth_t;
|
||||
|
||||
CRGB result = CRGB(
|
||||
a.r + (int)((b.r - a.r) * smooth_t),
|
||||
a.g + (int)((b.g - a.g) * smooth_t),
|
||||
a.b + (int)((b.b - a.b) * smooth_t)
|
||||
);
|
||||
|
||||
// Brightness compensation
|
||||
float current_brightness = (result.r + result.g + result.b) / 765.0f;
|
||||
if (current_brightness > 0.01f) {
|
||||
float compensation = target_brightness / current_brightness;
|
||||
compensation = min(compensation, 2.0f); // Limit boost
|
||||
result.r = min(255, (int)(result.r * compensation));
|
||||
result.g = min(255, (int)(result.g * compensation));
|
||||
result.b = min(255, (int)(result.b * compensation));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// ALL 10 Variant names for UI
|
||||
fl::string variant_names[10] = {
|
||||
"Cosmic Swirl", "Electric Storm", "Lava Lamp", "Digital Rain", "Plasma Waves",
|
||||
"Glitch City", "Ocean Depths", "Fire Dance", "Nebula Drift", "Binary Pulse"
|
||||
};
|
||||
|
||||
// 5 Color Palette names for UI
|
||||
fl::string palette_names[5] = {
|
||||
"Sunset Boulevard", "Ocean Breeze", "Neon Nights", "Forest Whisper", "Galaxy Express"
|
||||
};
|
||||
|
||||
// Helper functions to get indices from names
|
||||
uint8_t getVariantIndex(const fl::string& name) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
if (variant_names[i] == name) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0; // Default to first variant
|
||||
}
|
||||
|
||||
uint8_t getPaletteIndex(const fl::string& name) {
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (palette_names[i] == name) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0; // Default to first palette
|
||||
}
|
||||
|
||||
// Global instances - order matters for initialization
|
||||
ColorPaletteManager palette_manager;
|
||||
NoiseVariantManager variant_manager(palette_manager);
|
||||
RingLUT ring_lut;
|
||||
|
||||
// KICKASS UI controls - comprehensive control suite
|
||||
UISlider brightness("Brightness", 1, 0, 1);
|
||||
UISlider scale("Scale", 4, .1, 4, .1);
|
||||
UISlider timeBitshift("Time Bitshift", 5, 0, 16, 1);
|
||||
UISlider timescale("Time Scale", 1, .1, 10, .1);
|
||||
|
||||
// Advanced variant and palette controls
|
||||
UIDropdown variants("Noise Variants", variant_names);
|
||||
UIDropdown palettes("Color Palettes", palette_names);
|
||||
UICheckbox autoCycle("Auto Cycle Effects", true);
|
||||
UICheckbox autoPalette("Auto Cycle Palettes", true);
|
||||
// This PIR type is special because it will bind to a pin for a real device,
|
||||
// but also provides a UIButton when run in the simulator.
|
||||
Pir pir(PIN_PIR, PIR_LATCH_MS, PIR_RISING_TIME, PIR_FALLING_TIME);
|
||||
UICheckbox useDither("Use Binary Dither", true);
|
||||
|
||||
Timer timer;
|
||||
float current_brightness = 0;
|
||||
|
||||
// Save a pointer to the controller so that we can modify the dither in real time.
|
||||
CLEDController* controller = nullptr;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
// ScreenMap is purely something that is needed for the sketch to correctly
|
||||
// show on the web display. For deployements to real devices, this essentially
|
||||
// becomes a no-op.
|
||||
ScreenMap xyMap = ScreenMap::Circle(NUM_LEDS, 2.0, 2.0);
|
||||
controller = &FastLED.addLeds<WS2811, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS)
|
||||
.setCorrection(TypicalLEDStrip)
|
||||
.setDither(DISABLE_DITHER)
|
||||
.setScreenMap(xyMap);
|
||||
FastLED.setBrightness(brightness);
|
||||
pir.activate(millis()); // Activate the PIR sensor on startup.
|
||||
|
||||
// Initialize performance optimizations
|
||||
ring_lut.initialize();
|
||||
}
|
||||
|
||||
void draw(uint32_t now) {
|
||||
// Configure plasma parameters from UI controls with enhanced scaling
|
||||
PlasmaParams plasma_params;
|
||||
plasma_params.time_scale = timescale.as<float>();
|
||||
plasma_params.noise_intensity = scale.as<float>() * 0.8f; // Slightly reduce for better visual balance
|
||||
plasma_params.brightness = brightness.as<float>();
|
||||
plasma_params.time_bitshift = timeBitshift.as<int>();
|
||||
plasma_params.hue_offset = (now / 100) % 256; // Slow hue rotation for extra dynamism
|
||||
plasma_params.noise_amplitude = 0.6f + 0.4f * sin(now * 0.001f); // Breathing noise effect
|
||||
|
||||
// Update palette manager with auto-cycling and manual control
|
||||
palette_manager.update(now, autoPalette.value(), getPaletteIndex(palettes.value()));
|
||||
|
||||
// Update variant manager with enhanced parameters
|
||||
variant_manager.update(now, autoCycle.value(), getVariantIndex(variants.value()), plasma_params);
|
||||
|
||||
// KICKASS rendering with performance optimizations
|
||||
for (int i = 0; i < NUM_LEDS; i++) {
|
||||
RingCoord coord = ring_lut.fastRingCoord(i);
|
||||
CRGB pixel_color = variant_manager.renderPixel(coord, now);
|
||||
|
||||
// Apply global brightness and gamma correction for better visual quality
|
||||
float global_brightness = brightness.as<float>();
|
||||
pixel_color.r = (uint8_t)(pixel_color.r * global_brightness);
|
||||
pixel_color.g = (uint8_t)(pixel_color.g * global_brightness);
|
||||
pixel_color.b = (uint8_t)(pixel_color.b * global_brightness);
|
||||
|
||||
leds[i] = pixel_color;
|
||||
}
|
||||
|
||||
// Optional: Add subtle sparkle overlay for extra visual interest
|
||||
EVERY_N_MILLISECONDS(50) {
|
||||
// Add random sparkles to 1% of LEDs
|
||||
int sparkle_count = NUM_LEDS / 100 + 1;
|
||||
for (int s = 0; s < sparkle_count; s++) {
|
||||
int sparkle_pos = random16() % NUM_LEDS;
|
||||
if (random8() > 250) { // Very rare sparkles
|
||||
leds[sparkle_pos] = blend(leds[sparkle_pos], CRGB::White, 128);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Allow the dither to be enabled and disabled.
|
||||
controller->setDither(useDither ? BINARY_DITHER : DISABLE_DITHER);
|
||||
uint32_t now = millis();
|
||||
uint8_t bri = pir.transition(now);
|
||||
FastLED.setBrightness(bri * brightness.as<float>());
|
||||
// Apply leds generation to the leds.
|
||||
draw(now);
|
||||
|
||||
|
||||
FastLED.show();
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "fl/sketch_macros.h"
|
||||
|
||||
#if SKETCH_HAS_LOTS_OF_MEMORY
|
||||
|
||||
#include "FxNoiseRing.h"
|
||||
|
||||
#else
|
||||
void setup() {}
|
||||
void loop() {}
|
||||
|
||||
#endif // SKETCH_HAS_LOTS_OF_MEMORY
|
||||
@@ -0,0 +1,760 @@
|
||||
# NoiseRing Enhanced Design Document
|
||||
|
||||
## Overview
|
||||
Enhanced version of the FxNoiseRing example that automatically cycles through different noise effects and color palettes, providing dynamic visual variety with user controls for manual selection.
|
||||
|
||||
**Featured Implementation**: **Plasma Waves** - Advanced graphics technique showcasing sine wave interference with noise modulation, demonstrating sophisticated mathematical visualization on circular LED arrays.
|
||||
|
||||
## Core Features
|
||||
|
||||
### Automatic Cycling
|
||||
- **Palette Rotation**: Every 5 seconds using `EVERY_N_MILLISECONDS(5000)`
|
||||
- **Noise Effect Rotation**: Every 10 seconds using `EVERY_N_MILLISECONDS(10000)`
|
||||
- **User Override**: Dropdown controls allow manual selection to override automatic cycling
|
||||
|
||||
### User Interface Controls
|
||||
- **Variants Dropdown**: "Noise Variants" - Manual selection of noise effects (0-9)
|
||||
- **Palettes Dropdown**: "Color Palettes" - Manual selection of color schemes (0-4)
|
||||
- **Auto Cycle Checkbox**: "Auto Cycle" - Enable/disable automatic rotation
|
||||
- **Existing Controls**: Retain all current sliders (Brightness, Scale, Time Bitshift, Time Scale, PIR, Dither)
|
||||
|
||||
## 10 Noise Variations - Detailed Algorithmic Implementation
|
||||
|
||||
### 1. "Cosmic Swirl" - Enhanced Perlin Flow
|
||||
- **Description**: Classic perlin noise with slow, flowing movements using multi-octave complexity
|
||||
- **Parameters**: Base noise with moderate scale, gentle time progression
|
||||
- **Characteristics**: Smooth gradients, organic flow patterns
|
||||
|
||||
**Algorithm**:
|
||||
```cpp
|
||||
CRGB drawCosmicSwirl(const RingCoord& coord, uint32_t time_ms) {
|
||||
float time_factor = time_ms * 0.0008f;
|
||||
|
||||
// Multi-octave noise for organic complexity
|
||||
float noise1 = inoise16(coord.x * 2000, coord.y * 2000, time_factor * 1000) / 65536.0f;
|
||||
float noise2 = inoise16(coord.x * 1000, coord.y * 1000, time_factor * 2000) / 65536.0f * 0.5f;
|
||||
float noise3 = inoise16(coord.x * 4000, coord.y * 4000, time_factor * 500) / 65536.0f * 0.25f;
|
||||
|
||||
float combined_noise = noise1 + noise2 + noise3;
|
||||
|
||||
// Flowing hue with gentle progression
|
||||
uint8_t hue = (uint8_t)((combined_noise + coord.angle / (2*M_PI)) * 255) % 256;
|
||||
uint8_t sat = 220 + (uint8_t)(abs(noise2) * 35);
|
||||
uint8_t val = 180 + (uint8_t)(combined_noise * 75);
|
||||
|
||||
return CHSV(hue, sat, val);
|
||||
}
|
||||
```
|
||||
|
||||
### 2. "Electric Storm" - High-Frequency Chaos
|
||||
- **Description**: High-frequency noise with rapid temporal changes creating lightning effects
|
||||
- **Parameters**: 8x time acceleration, high spatial frequency, quantized thresholds
|
||||
- **Characteristics**: Crackling, energetic, lightning-like patterns
|
||||
|
||||
**Algorithm**:
|
||||
```cpp
|
||||
CRGB drawElectricStorm(const RingCoord& coord, uint32_t time_ms) {
|
||||
// Rapid temporal changes with quantized effects
|
||||
uint32_t fast_time = time_ms << 3; // 8x time acceleration
|
||||
|
||||
// High-frequency spatial noise
|
||||
float x_noise = coord.x * 8000;
|
||||
float y_noise = coord.y * 8000;
|
||||
|
||||
uint16_t noise1 = inoise16(x_noise, y_noise, fast_time);
|
||||
uint16_t noise2 = inoise16(x_noise + 10000, y_noise + 10000, fast_time + 5000);
|
||||
|
||||
// Create lightning-like quantization
|
||||
uint8_t threshold = 200;
|
||||
bool lightning = (noise1 >> 8) > threshold || (noise2 >> 8) > threshold;
|
||||
|
||||
if (lightning) {
|
||||
// Bright electric flash
|
||||
uint8_t intensity = max((noise1 >> 8) - threshold, (noise2 >> 8) - threshold) * 4;
|
||||
return CRGB(intensity, intensity, 255); // Electric blue-white
|
||||
} else {
|
||||
// Dark storm background
|
||||
uint8_t hue = 160 + ((noise1 >> 10) % 32); // Blue-purple range
|
||||
return CHSV(hue, 255, (noise1 >> 8) / 4); // Low brightness
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. "Lava Lamp" - Slow Blobby Movement
|
||||
- **Description**: Slow, blobby movements with high contrast using low-frequency modulation
|
||||
- **Parameters**: Ultra-low frequency, high amplitude, threshold-based blob creation
|
||||
- **Characteristics**: Large, slow-moving color blobs with organic boundaries
|
||||
|
||||
**Algorithm**:
|
||||
```cpp
|
||||
CRGB drawLavaLamp(const RingCoord& coord, uint32_t time_ms) {
|
||||
float slow_time = time_ms * 0.0002f; // Very slow movement
|
||||
|
||||
// Large-scale blob generation
|
||||
float blob_scale = 800; // Large spatial scale for big blobs
|
||||
uint16_t primary_noise = inoise16(coord.x * blob_scale, coord.y * blob_scale, slow_time * 1000);
|
||||
uint16_t secondary_noise = inoise16(coord.x * blob_scale * 0.5f, coord.y * blob_scale * 0.5f, slow_time * 1500);
|
||||
|
||||
// Create blob boundaries with thresholding
|
||||
float blob_value = (primary_noise + secondary_noise * 0.3f) / 65536.0f;
|
||||
|
||||
// High contrast blob regions
|
||||
if (blob_value > 0.6f) {
|
||||
// Hot blob center
|
||||
uint8_t hue = 0 + (uint8_t)((blob_value - 0.6f) * 400); // Red to orange
|
||||
return CHSV(hue, 255, 255);
|
||||
} else if (blob_value > 0.3f) {
|
||||
// Blob edge gradient
|
||||
float edge_factor = (blob_value - 0.3f) / 0.3f;
|
||||
uint8_t brightness = (uint8_t)(edge_factor * 255);
|
||||
return CHSV(20, 200, brightness); // Orange edge
|
||||
} else {
|
||||
// Background
|
||||
return CHSV(240, 100, 30); // Dark blue background
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. "Digital Rain" - Matrix Cascade
|
||||
- **Description**: Matrix-style cascading effect using vertical noise mapping
|
||||
- **Parameters**: Angle-to-vertical conversion, time-based cascade, stream segregation
|
||||
- **Characteristics**: Vertical streams, binary-like transitions, matrix green
|
||||
|
||||
**Algorithm**:
|
||||
```cpp
|
||||
CRGB drawDigitalRain(const RingCoord& coord, uint32_t time_ms) {
|
||||
// Convert angle to vertical position for cascade effect
|
||||
float vertical_pos = sin(coord.angle) * 0.5f + 0.5f; // 0-1 range
|
||||
|
||||
// Time-based cascade with varying speeds
|
||||
float cascade_speed = 0.002f;
|
||||
float time_offset = time_ms * cascade_speed;
|
||||
|
||||
// Create vertical streams
|
||||
int stream_id = (int)(coord.angle * 10) % 8; // 8 distinct streams
|
||||
float stream_phase = fmod(vertical_pos + time_offset + stream_id * 0.125f, 1.0f);
|
||||
|
||||
// Binary-like transitions
|
||||
uint16_t noise = inoise16(stream_id * 1000, stream_phase * 10000, time_ms / 4);
|
||||
uint8_t digital_value = (noise >> 8) > 128 ? 255 : 0;
|
||||
|
||||
// Matrix green with digital artifacts
|
||||
uint8_t green_intensity = digital_value;
|
||||
uint8_t trailing = max(0, green_intensity - (int)(stream_phase * 200));
|
||||
|
||||
return CRGB(0, green_intensity, trailing / 2);
|
||||
}
|
||||
```
|
||||
|
||||
### 5. "Plasma Waves" - **FEATURED IMPLEMENTATION**
|
||||
- **Description**: Multiple overlapping sine waves with noise modulation creating electromagnetic plasma effects
|
||||
- **Parameters**: 4-source wave interference, noise modulation, dynamic color mapping
|
||||
- **Characteristics**: Smooth wave interference patterns, flowing electromagnetic appearance
|
||||
|
||||
**Algorithm**:
|
||||
```cpp
|
||||
class PlasmaWaveGenerator {
|
||||
private:
|
||||
struct WaveSource {
|
||||
float x, y; // Source position
|
||||
float frequency; // Wave frequency
|
||||
float amplitude; // Wave strength
|
||||
float phase_speed; // Phase evolution rate
|
||||
};
|
||||
|
||||
WaveSource sources[4] = {
|
||||
{0.5f, 0.5f, 1.0f, 1.0f, 0.8f}, // Center source
|
||||
{0.0f, 0.0f, 1.5f, 0.8f, 1.2f}, // Corner source
|
||||
{1.0f, 1.0f, 0.8f, 1.2f, 0.6f}, // Opposite corner
|
||||
{0.5f, 0.0f, 1.2f, 0.9f, 1.0f} // Edge source
|
||||
};
|
||||
|
||||
public:
|
||||
CRGB calculatePlasmaPixel(const RingCoord& coord, uint32_t time_ms, const PlasmaParams& params) {
|
||||
float time_scaled = time_ms * params.time_scale * 0.001f;
|
||||
|
||||
// Calculate wave interference
|
||||
float wave_sum = 0.0f;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
float dx = coord.x - sources[i].x;
|
||||
float dy = coord.y - sources[i].y;
|
||||
float distance = sqrt(dx*dx + dy*dy);
|
||||
|
||||
float wave_phase = distance * sources[i].frequency + time_scaled * sources[i].phase_speed;
|
||||
wave_sum += sin(wave_phase) * sources[i].amplitude;
|
||||
}
|
||||
|
||||
// Add noise modulation for organic feel
|
||||
float noise_scale = params.noise_intensity;
|
||||
float noise_x = coord.x * 0xffff * noise_scale;
|
||||
float noise_y = coord.y * 0xffff * noise_scale;
|
||||
uint32_t noise_time = time_ms << params.time_bitshift;
|
||||
|
||||
float noise_mod = (inoise16(noise_x, noise_y, noise_time) - 32768) / 65536.0f;
|
||||
wave_sum += noise_mod * params.noise_amplitude;
|
||||
|
||||
// Map to color space
|
||||
return mapWaveToColor(wave_sum, params);
|
||||
}
|
||||
|
||||
private:
|
||||
CRGB mapWaveToColor(float wave_value, const PlasmaParams& params) {
|
||||
// Normalize wave to 0-1 range
|
||||
float normalized = (wave_value + 4.0f) / 8.0f; // Assuming max amplitude ~4
|
||||
normalized = constrain(normalized, 0.0f, 1.0f);
|
||||
|
||||
// Create flowing hue based on wave phase
|
||||
uint8_t hue = (uint8_t)(normalized * 255.0f + params.hue_offset) % 256;
|
||||
|
||||
// Dynamic saturation based on wave intensity
|
||||
float intensity = abs(wave_value);
|
||||
uint8_t sat = (uint8_t)(192 + intensity * 63); // High saturation with variation
|
||||
|
||||
// Brightness modulation
|
||||
uint8_t val = (uint8_t)(normalized * 255.0f * params.brightness);
|
||||
|
||||
return CHSV(hue, sat, val);
|
||||
}
|
||||
};
|
||||
|
||||
struct PlasmaParams {
|
||||
float time_scale = 1.0f;
|
||||
float noise_intensity = 0.5f;
|
||||
float noise_amplitude = 0.8f;
|
||||
uint8_t time_bitshift = 5;
|
||||
uint8_t hue_offset = 0;
|
||||
float brightness = 1.0f;
|
||||
};
|
||||
```
|
||||
|
||||
### 6. "Glitch City" - Chaotic Digital Artifacts
|
||||
- **Description**: Chaotic, stuttering effects with quantized noise and bit manipulation
|
||||
- **Parameters**: Time quantization, XOR operations, random bit shifts
|
||||
- **Characteristics**: Harsh transitions, digital artifacts, strobe-like effects
|
||||
|
||||
**Algorithm**:
|
||||
```cpp
|
||||
CRGB drawGlitchCity(const RingCoord& coord, uint32_t time_ms) {
|
||||
// Stuttering time progression
|
||||
uint32_t glitch_time = (time_ms / 100) * 100; // Quantize time to create stutters
|
||||
|
||||
// Bit manipulation for digital artifacts
|
||||
uint16_t noise1 = inoise16(coord.x * 3000, coord.y * 3000, glitch_time);
|
||||
uint16_t noise2 = inoise16(coord.x * 5000, coord.y * 5000, glitch_time + 1000);
|
||||
|
||||
// XOR operation for harsh digital effects
|
||||
uint16_t glitch_value = noise1 ^ noise2;
|
||||
|
||||
// Random bit shifts for channel corruption
|
||||
uint8_t r = (glitch_value >> (time_ms % 8)) & 0xFF;
|
||||
uint8_t g = (glitch_value << (time_ms % 5)) & 0xFF;
|
||||
uint8_t b = ((noise1 | noise2) >> 4) & 0xFF;
|
||||
|
||||
// Occasional full-bright flashes
|
||||
if ((glitch_value & 0xF000) == 0xF000) {
|
||||
return CRGB(255, 255, 255);
|
||||
}
|
||||
|
||||
return CRGB(r, g, b);
|
||||
}
|
||||
```
|
||||
|
||||
### 7. "Ocean Depths" - Underwater Currents
|
||||
- **Description**: Slow, deep undulations mimicking underwater currents with blue-green bias
|
||||
- **Parameters**: Ultra-low frequency, blue-green color bias, depth-based brightness
|
||||
- **Characteristics**: Calm, flowing, deep water feel with gentle undulations
|
||||
|
||||
**Algorithm**:
|
||||
```cpp
|
||||
CRGB drawOceanDepths(const RingCoord& coord, uint32_t time_ms) {
|
||||
float ocean_time = time_ms * 0.0005f; // Very slow like deep water
|
||||
|
||||
// Multi-layer current simulation
|
||||
float current1 = inoise16(coord.x * 1200, coord.y * 1200, ocean_time * 800) / 65536.0f;
|
||||
float current2 = inoise16(coord.x * 2400, coord.y * 2400, ocean_time * 600) / 65536.0f * 0.5f;
|
||||
float current3 = inoise16(coord.x * 600, coord.y * 600, ocean_time * 1000) / 65536.0f * 0.3f;
|
||||
|
||||
float depth_factor = current1 + current2 + current3;
|
||||
|
||||
// Ocean color palette (blue-green spectrum)
|
||||
uint8_t base_hue = 140; // Cyan-blue
|
||||
uint8_t hue_variation = (uint8_t)(abs(depth_factor) * 40); // Vary within blue-green
|
||||
uint8_t final_hue = (base_hue + hue_variation) % 256;
|
||||
|
||||
// Depth-based brightness (deeper = darker)
|
||||
uint8_t depth_brightness = 120 + (uint8_t)(depth_factor * 135);
|
||||
uint8_t saturation = 200 + (uint8_t)(abs(current2) * 55);
|
||||
|
||||
return CHSV(final_hue, saturation, depth_brightness);
|
||||
}
|
||||
```
|
||||
|
||||
### 8. "Fire Dance" - Upward Flame Simulation
|
||||
- **Description**: Flickering, flame-like patterns with upward bias and turbulent noise
|
||||
- **Parameters**: Vertical gradient bias, turbulent noise, fire color palette
|
||||
- **Characteristics**: Orange/red dominated, upward movement, flickering
|
||||
|
||||
**Algorithm**:
|
||||
```cpp
|
||||
CRGB drawFireDance(const RingCoord& coord, uint32_t time_ms) {
|
||||
// Vertical bias for upward flame movement
|
||||
float vertical_component = sin(coord.angle) * 0.5f + 0.5f; // 0 at bottom, 1 at top
|
||||
|
||||
// Turbulent noise with upward bias
|
||||
float flame_x = coord.x * 1500;
|
||||
float flame_y = coord.y * 1500 + time_ms * 0.003f; // Upward drift
|
||||
|
||||
uint16_t turbulence = inoise16(flame_x, flame_y, time_ms);
|
||||
float flame_intensity = (turbulence / 65536.0f) * (1.0f - vertical_component * 0.3f);
|
||||
|
||||
// Fire color palette (red->orange->yellow)
|
||||
uint8_t base_hue = 0; // Red
|
||||
uint8_t hue_variation = (uint8_t)(flame_intensity * 45); // Up to orange/yellow
|
||||
uint8_t final_hue = (base_hue + hue_variation) % 256;
|
||||
|
||||
uint8_t saturation = 255 - (uint8_t)(vertical_component * 100); // Less saturated at top
|
||||
uint8_t brightness = (uint8_t)(flame_intensity * 255);
|
||||
|
||||
return CHSV(final_hue, saturation, brightness);
|
||||
}
|
||||
```
|
||||
|
||||
### 9. "Nebula Drift" - Cosmic Cloud Simulation
|
||||
- **Description**: Slow cosmic clouds with starfield sparkles using multi-octave noise
|
||||
- **Parameters**: Multiple noise octaves, sparse bright spots, cosmic color palette
|
||||
- **Characteristics**: Misty backgrounds with occasional bright stars
|
||||
|
||||
**Algorithm**:
|
||||
```cpp
|
||||
CRGB drawNebulaDrift(const RingCoord& coord, uint32_t time_ms) {
|
||||
float nebula_time = time_ms * 0.0003f; // Cosmic slow drift
|
||||
|
||||
// Multi-octave nebula clouds
|
||||
float cloud1 = inoise16(coord.x * 800, coord.y * 800, nebula_time * 1000) / 65536.0f;
|
||||
float cloud2 = inoise16(coord.x * 1600, coord.y * 1600, nebula_time * 700) / 65536.0f * 0.5f;
|
||||
float cloud3 = inoise16(coord.x * 400, coord.y * 400, nebula_time * 1200) / 65536.0f * 0.25f;
|
||||
|
||||
float nebula_density = cloud1 + cloud2 + cloud3;
|
||||
|
||||
// Sparse starfield generation
|
||||
uint16_t star_noise = inoise16(coord.x * 4000, coord.y * 4000, nebula_time * 200);
|
||||
bool is_star = (star_noise > 60000); // Very sparse stars
|
||||
|
||||
if (is_star) {
|
||||
// Bright white/blue stars
|
||||
uint8_t star_brightness = 200 + ((star_noise - 60000) / 256);
|
||||
return CRGB(star_brightness, star_brightness, 255);
|
||||
} else {
|
||||
// Nebula background
|
||||
uint8_t nebula_hue = 200 + (uint8_t)(nebula_density * 80); // Purple-pink spectrum
|
||||
uint8_t nebula_sat = 150 + (uint8_t)(abs(cloud2) * 105);
|
||||
uint8_t nebula_bright = 40 + (uint8_t)(nebula_density * 120);
|
||||
|
||||
return CHSV(nebula_hue, nebula_sat, nebula_bright);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 10. "Binary Pulse" - Digital Heartbeat
|
||||
- **Description**: Digital heartbeat with expanding/contracting rings using threshold-based noise
|
||||
- **Parameters**: Concentric pattern generation, rhythmic pulsing, geometric thresholds
|
||||
- **Characteristics**: Rhythmic, geometric, tech-inspired
|
||||
|
||||
**Algorithm**:
|
||||
```cpp
|
||||
CRGB drawBinaryPulse(const RingCoord& coord, uint32_t time_ms) {
|
||||
// Create rhythmic heartbeat timing
|
||||
float pulse_period = 2000.0f; // 2-second pulse cycle
|
||||
float pulse_phase = fmod(time_ms, pulse_period) / pulse_period; // 0-1 cycle
|
||||
|
||||
// Generate expanding rings from center
|
||||
float distance_from_center = sqrt(coord.x * coord.x + coord.y * coord.y);
|
||||
|
||||
// Pulse wave propagation
|
||||
float ring_frequency = 5.0f; // Number of rings
|
||||
float pulse_offset = pulse_phase * 2.0f; // Expanding wave
|
||||
float ring_value = sin((distance_from_center * ring_frequency - pulse_offset) * 2 * M_PI);
|
||||
|
||||
// Digital quantization
|
||||
uint16_t noise = inoise16(coord.x * 2000, coord.y * 2000, time_ms / 8);
|
||||
float digital_mod = ((noise >> 8) > 128) ? 1.0f : -0.5f;
|
||||
|
||||
float final_value = ring_value * digital_mod;
|
||||
|
||||
// Binary color mapping
|
||||
if (final_value > 0.3f) {
|
||||
// Active pulse regions
|
||||
uint8_t intensity = (uint8_t)(final_value * 255);
|
||||
return CRGB(intensity, 0, intensity); // Magenta pulse
|
||||
} else if (final_value > -0.2f) {
|
||||
// Transition zones
|
||||
uint8_t dim_intensity = (uint8_t)((final_value + 0.2f) * 500);
|
||||
return CRGB(0, dim_intensity, 0); // Green transitions
|
||||
} else {
|
||||
// Background
|
||||
return CRGB(10, 0, 20); // Dark purple background
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 5 Color Palettes
|
||||
|
||||
### 1. "Sunset Boulevard"
|
||||
- **Colors**: Warm oranges, deep reds, golden yellows
|
||||
- **Description**: Classic sunset gradient perfect for relaxing ambiance
|
||||
- **HSV Range**: Hue 0-45, high saturation, varying brightness
|
||||
|
||||
### 2. "Ocean Breeze"
|
||||
- **Colors**: Deep blues, aqua, seafoam green, white caps
|
||||
- **Description**: Cool ocean palette for refreshing visual effects
|
||||
- **HSV Range**: Hue 120-210, medium-high saturation
|
||||
|
||||
### 3. "Neon Nights"
|
||||
- **Colors**: Electric pink, cyan, purple, lime green
|
||||
- **Description**: Cyberpunk-inspired high-contrast palette
|
||||
- **HSV Range**: Saturated primaries, high brightness contrasts
|
||||
|
||||
### 4. "Forest Whisper"
|
||||
- **Colors**: Deep greens, earth browns, golden highlights
|
||||
- **Description**: Natural woodland palette for organic feels
|
||||
- **HSV Range**: Hue 60-150, natural saturation levels
|
||||
|
||||
### 5. "Galaxy Express"
|
||||
- **Colors**: Deep purples, cosmic blues, silver stars, pink nebula
|
||||
- **Description**: Space-themed palette for cosmic adventures
|
||||
- **HSV Range**: Hue 200-300, with bright white accents
|
||||
|
||||
## Implementation Strategy
|
||||
|
||||
### Core Mathematical Framework
|
||||
|
||||
```cpp
|
||||
// Enhanced coordinate system for ring-based effects
|
||||
struct RingCoord {
|
||||
float angle; // Position on ring (0 to 2π)
|
||||
float radius; // Distance from center (normalized 0-1)
|
||||
float x, y; // Cartesian coordinates
|
||||
int led_index; // LED position on strip
|
||||
};
|
||||
|
||||
// Convert LED index to ring coordinates
|
||||
RingCoord calculateRingCoord(int led_index, int num_leds, float time_offset = 0.0f) {
|
||||
RingCoord coord;
|
||||
coord.led_index = led_index;
|
||||
coord.angle = (led_index * 2.0f * M_PI / num_leds) + time_offset;
|
||||
coord.radius = 1.0f; // Fixed radius for ring
|
||||
coord.x = cos(coord.angle);
|
||||
coord.y = sin(coord.angle);
|
||||
return coord;
|
||||
}
|
||||
|
||||
// Performance optimization with lookup tables
|
||||
class RingLUT {
|
||||
private:
|
||||
float cos_table[NUM_LEDS];
|
||||
float sin_table[NUM_LEDS];
|
||||
|
||||
public:
|
||||
void initialize() {
|
||||
for(int i = 0; i < NUM_LEDS; i++) {
|
||||
float angle = i * 2.0f * M_PI / NUM_LEDS;
|
||||
cos_table[i] = cos(angle);
|
||||
sin_table[i] = sin(angle);
|
||||
}
|
||||
}
|
||||
|
||||
RingCoord fastRingCoord(int led_index, float time_offset = 0.0f) {
|
||||
RingCoord coord;
|
||||
coord.led_index = led_index;
|
||||
coord.angle = (led_index * 2.0f * M_PI / NUM_LEDS) + time_offset;
|
||||
coord.x = cos_table[led_index];
|
||||
coord.y = sin_table[led_index];
|
||||
coord.radius = 1.0f;
|
||||
return coord;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Enhanced Control System with Smooth Transitions
|
||||
|
||||
```cpp
|
||||
class NoiseVariantManager {
|
||||
private:
|
||||
uint8_t current_variant = 0;
|
||||
uint8_t target_variant = 0;
|
||||
float transition_progress = 1.0f; // 0.0 = old, 1.0 = new
|
||||
uint32_t transition_start = 0;
|
||||
static const uint32_t TRANSITION_DURATION = 1000; // 1 second fade
|
||||
|
||||
// Algorithm instances
|
||||
PlasmaWaveGenerator plasma_gen;
|
||||
PlasmaParams plasma_params;
|
||||
|
||||
public:
|
||||
void update(uint32_t now, bool auto_cycle_enabled, uint8_t manual_variant) {
|
||||
// Handle automatic cycling vs manual override
|
||||
if (auto_cycle_enabled) {
|
||||
EVERY_N_MILLISECONDS(10000) {
|
||||
startTransition((current_variant + 1) % 10, now);
|
||||
}
|
||||
} else if (manual_variant != target_variant && transition_progress >= 1.0f) {
|
||||
// Manual override
|
||||
startTransition(manual_variant, now);
|
||||
}
|
||||
|
||||
// Update transition progress
|
||||
if (transition_progress < 1.0f) {
|
||||
uint32_t elapsed = now - transition_start;
|
||||
transition_progress = min(1.0f, elapsed / (float)TRANSITION_DURATION);
|
||||
|
||||
if (transition_progress >= 1.0f) {
|
||||
current_variant = target_variant;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CRGB renderPixel(const RingCoord& coord, uint32_t time_ms) {
|
||||
if (transition_progress >= 1.0f) {
|
||||
// No transition, render current variant
|
||||
return renderVariant(current_variant, coord, time_ms);
|
||||
} else {
|
||||
// Blend between variants
|
||||
CRGB old_color = renderVariant(current_variant, coord, time_ms);
|
||||
CRGB new_color = renderVariant(target_variant, coord, time_ms);
|
||||
return lerpCRGB(old_color, new_color, transition_progress);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void startTransition(uint8_t new_variant, uint32_t now) {
|
||||
target_variant = new_variant;
|
||||
transition_start = now;
|
||||
transition_progress = 0.0f;
|
||||
}
|
||||
|
||||
CRGB renderVariant(uint8_t variant, const RingCoord& coord, uint32_t time_ms) {
|
||||
switch(variant) {
|
||||
case 0: return drawCosmicSwirl(coord, time_ms);
|
||||
case 1: return drawElectricStorm(coord, time_ms);
|
||||
case 2: return drawLavaLamp(coord, time_ms);
|
||||
case 3: return drawDigitalRain(coord, time_ms);
|
||||
case 4: return plasma_gen.calculatePlasmaPixel(coord, time_ms, plasma_params);
|
||||
case 5: return drawGlitchCity(coord, time_ms);
|
||||
case 6: return drawOceanDepths(coord, time_ms);
|
||||
case 7: return drawFireDance(coord, time_ms);
|
||||
case 8: return drawNebulaDrift(coord, time_ms);
|
||||
case 9: return drawBinaryPulse(coord, time_ms);
|
||||
default: return CRGB::Black;
|
||||
}
|
||||
}
|
||||
|
||||
CRGB lerpCRGB(const CRGB& a, const CRGB& b, float t) {
|
||||
return CRGB(
|
||||
a.r + (int)((b.r - a.r) * t),
|
||||
a.g + (int)((b.g - a.g) * t),
|
||||
a.b + (int)((b.b - a.b) * t)
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Data Structures and UI Integration
|
||||
|
||||
```cpp
|
||||
// Enhanced data structures
|
||||
String variant_names[10] = {
|
||||
"Cosmic Swirl", "Electric Storm", "Lava Lamp", "Digital Rain", "Plasma Waves",
|
||||
"Glitch City", "Ocean Depths", "Fire Dance", "Nebula Drift", "Binary Pulse"
|
||||
};
|
||||
|
||||
String palette_names[5] = {
|
||||
"Sunset Boulevard", "Ocean Breeze", "Neon Nights", "Forest Whisper", "Galaxy Express"
|
||||
};
|
||||
|
||||
// Global instances
|
||||
NoiseVariantManager variant_manager;
|
||||
RingLUT ring_lut;
|
||||
|
||||
// Enhanced UI controls
|
||||
UIDropdown variants("Noise Variants", variant_names, 10);
|
||||
UIDropdown palettes("Color Palettes", palette_names, 5);
|
||||
UICheckbox autoCycle("Auto Cycle", true);
|
||||
```
|
||||
|
||||
### Integration with Existing Framework
|
||||
|
||||
```cpp
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
ScreenMap xyMap = ScreenMap::Circle(NUM_LEDS, 2.0, 2.0);
|
||||
controller = &FastLED.addLeds<WS2811, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS)
|
||||
.setCorrection(TypicalLEDStrip)
|
||||
.setDither(DISABLE_DITHER)
|
||||
.setScreenMap(xyMap);
|
||||
FastLED.setBrightness(brightness);
|
||||
pir.activate(millis());
|
||||
|
||||
// Initialize performance optimizations
|
||||
ring_lut.initialize();
|
||||
}
|
||||
|
||||
void draw(uint32_t now) {
|
||||
// Update variant manager
|
||||
variant_manager.update(now, autoCycle.value(), variants.value());
|
||||
|
||||
// Render each LED with current variant
|
||||
for (int i = 0; i < NUM_LEDS; i++) {
|
||||
RingCoord coord = ring_lut.fastRingCoord(i);
|
||||
leds[i] = variant_manager.renderPixel(coord, now);
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
controller->setDither(useDither ? BINARY_DITHER : DISABLE_DITHER);
|
||||
uint32_t now = millis();
|
||||
uint8_t bri = pir.transition(now);
|
||||
FastLED.setBrightness(bri * brightness.as<float>());
|
||||
|
||||
draw(now);
|
||||
FastLED.show();
|
||||
}
|
||||
```
|
||||
|
||||
## Technical Considerations
|
||||
|
||||
### Performance Optimization
|
||||
- **Lookup Table Pre-computation**: Pre-calculate trigonometric values for ring positions
|
||||
- **Fixed-Point Arithmetic**: Use integer math where possible for embedded systems
|
||||
- **Noise Caching**: Cache noise parameters between frames for consistent animation
|
||||
- **Memory-Efficient Algorithms**: Optimize noise calculations for real-time performance
|
||||
- **Parallel Processing**: Structure algorithms for potential multi-core optimization
|
||||
|
||||
### Memory Management
|
||||
- **PROGMEM Storage**: Store palettes and static data in program memory for Arduino compatibility
|
||||
- **Dynamic Allocation Avoidance**: Minimize heap usage during effect transitions
|
||||
- **Stack Optimization**: Use local variables efficiently in nested algorithm calls
|
||||
- **Buffer Management**: Reuse coordinate calculation buffers where possible
|
||||
|
||||
### Mathematical Precision
|
||||
- **16-bit Noise Space**: Maintain precision in noise calculations before 8-bit mapping
|
||||
- **Floating Point Efficiency**: Balance precision vs. performance based on target platform
|
||||
- **Color Space Optimization**: Use HSV for smooth transitions, RGB for final output
|
||||
- **Numerical Stability**: Prevent overflow/underflow in wave interference calculations
|
||||
|
||||
### User Experience
|
||||
- **Smooth Transitions**: 1-second cross-fade between effects using linear interpolation
|
||||
- **Responsive Controls**: Immediate override of automatic cycling via manual selection
|
||||
- **Visual Feedback**: Clear indication of current variant and palette selection
|
||||
- **Performance Consistency**: Maintain stable frame rate across all effect variants
|
||||
|
||||
## First Pass Implementation: Plasma Waves
|
||||
|
||||
### Why Start with Plasma Waves?
|
||||
1. **Visual Impact**: Most impressive demonstration of advanced graphics programming
|
||||
2. **Mathematical Showcase**: Demonstrates sine wave interference and noise modulation
|
||||
3. **Building Foundation**: Establishes the RingCoord system used by all other variants
|
||||
4. **Performance Baseline**: Tests the most computationally intensive algorithm first
|
||||
|
||||
### Development Strategy
|
||||
```cpp
|
||||
// Phase 1: Core Infrastructure
|
||||
void setupPlasmaDemo() {
|
||||
// Initialize basic ring coordinate system
|
||||
ring_lut.initialize();
|
||||
|
||||
// Configure plasma parameters
|
||||
plasma_params.time_scale = timescale.as<float>();
|
||||
plasma_params.noise_intensity = scale.as<float>();
|
||||
plasma_params.brightness = brightness.as<float>();
|
||||
}
|
||||
|
||||
// Phase 2: Plasma-Only Implementation
|
||||
void drawPlasmaOnly(uint32_t now) {
|
||||
for (int i = 0; i < NUM_LEDS; i++) {
|
||||
RingCoord coord = ring_lut.fastRingCoord(i);
|
||||
leds[i] = plasma_gen.calculatePlasmaPixel(coord, now, plasma_params);
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 3: Add Manual Variants (No Auto-Cycling Yet)
|
||||
void drawWithManualSelection(uint32_t now) {
|
||||
uint8_t selected_variant = variants.value();
|
||||
|
||||
for (int i = 0; i < NUM_LEDS; i++) {
|
||||
RingCoord coord = ring_lut.fastRingCoord(i);
|
||||
|
||||
switch(selected_variant) {
|
||||
case 0: leds[i] = drawCosmicSwirl(coord, now); break;
|
||||
case 1: leds[i] = plasma_gen.calculatePlasmaPixel(coord, now, plasma_params); break;
|
||||
// Add variants incrementally
|
||||
default: leds[i] = plasma_gen.calculatePlasmaPixel(coord, now, plasma_params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 4: Full System with Auto-Cycling and Transitions
|
||||
void drawFullSystem(uint32_t now) {
|
||||
variant_manager.update(now, autoCycle.value(), variants.value());
|
||||
|
||||
for (int i = 0; i < NUM_LEDS; i++) {
|
||||
RingCoord coord = ring_lut.fastRingCoord(i);
|
||||
leds[i] = variant_manager.renderPixel(coord, now);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Testing and Validation
|
||||
1. **Plasma Waves Only**: Verify smooth wave interference and noise modulation
|
||||
2. **Parameter Responsiveness**: Test all UI sliders affect plasma generation correctly
|
||||
3. **Performance Metrics**: Measure frame rate with plasma algorithm on target hardware
|
||||
4. **Visual Quality**: Confirm smooth color transitions and no artifacts
|
||||
5. **Memory Usage**: Monitor RAM consumption during plasma calculations
|
||||
|
||||
### Incremental Development Plan
|
||||
1. **Week 1**: Implement Plasma Waves algorithm and RingCoord system
|
||||
2. **Week 2**: Add 2-3 simpler variants (Cosmic Swirl, Electric Storm, Fire Dance)
|
||||
3. **Week 3**: Implement transition system and automatic cycling
|
||||
4. **Week 4**: Add remaining variants and color palette system
|
||||
5. **Week 5**: Optimization, polish, and platform-specific tuning
|
||||
|
||||
## Advanced Graphics Techniques Demonstrated
|
||||
|
||||
### Wave Interference Mathematics
|
||||
The plasma algorithm showcases classical physics simulation:
|
||||
- **Superposition Principle**: Multiple wave sources combine linearly
|
||||
- **Phase Relationships**: Time-varying phase creates animation
|
||||
- **Distance-Based Attenuation**: Realistic wave propagation modeling
|
||||
- **Noise Modulation**: Organic variation through Perlin noise overlay
|
||||
|
||||
### Color Theory Implementation
|
||||
- **HSV Color Space**: Smooth hue transitions for natural color flow
|
||||
- **Saturation Modulation**: Dynamic saturation based on wave intensity
|
||||
- **Brightness Mapping**: Normalized wave values to brightness curves
|
||||
- **Gamma Correction**: Perceptually linear brightness progression
|
||||
|
||||
### Performance Optimization Strategies
|
||||
- **Trigonometric Lookup**: Pre-computed sine/cosine tables
|
||||
- **Fixed-Point Math**: Integer approximations for embedded platforms
|
||||
- **Loop Unrolling**: Minimize function call overhead in tight loops
|
||||
- **Memory Access Patterns**: Cache-friendly coordinate calculations
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### Advanced Features
|
||||
- **Save/Load Configurations**: User-defined effect combinations and parameters
|
||||
- **BPM Synchronization**: Music-reactive timing for effect transitions
|
||||
- **Custom Palette Editor**: User-defined color schemes with preview
|
||||
- **Effect Intensity Controls**: Per-variant amplitude and speed modulation
|
||||
- **Multi-Ring Support**: Expand to multiple concentric LED rings
|
||||
|
||||
### Platform Extensions
|
||||
- **Multi-Core Optimization**: Parallel processing for complex calculations
|
||||
- **GPU Acceleration**: WebGL compute shaders for web platform
|
||||
- **Hardware Acceleration**: Platform-specific optimizations (ESP32, Teensy)
|
||||
- **Memory Mapping**: Direct hardware buffer access for maximum performance
|
||||
|
||||
### Algorithm Enhancements
|
||||
- **Physically-Based Rendering**: More realistic light simulation
|
||||
- **Particle Systems**: Dynamic particle-based effects
|
||||
- **Fractal Algorithms**: Mandelbrot and Julia set visualizations
|
||||
- **Audio Visualization**: Spectrum analysis and reactive algorithms
|
||||
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include "fl/stdint.h"
|
||||
|
||||
/**
|
||||
* @brief A simple timer utility class for tracking timed events
|
||||
*
|
||||
* This class provides basic timer functionality for animations and effects.
|
||||
* It can be used to track whether a specific duration has elapsed since
|
||||
* the timer was started.
|
||||
*/
|
||||
class Timer {
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new Timer object
|
||||
*
|
||||
* Creates a timer in the stopped state with zero duration.
|
||||
*/
|
||||
Timer() : start_time(0), duration(0), running(false) {}
|
||||
|
||||
/**
|
||||
* @brief Start the timer with a specific duration
|
||||
*
|
||||
* @param now Current time in milliseconds (typically from millis())
|
||||
* @param duration How long the timer should run in milliseconds
|
||||
*/
|
||||
void start(uint32_t now, uint32_t duration) {
|
||||
start_time = now;
|
||||
this->duration = duration;
|
||||
running = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the timer state based on current time
|
||||
*
|
||||
* Checks if the timer is still running based on the current time.
|
||||
* If the specified duration has elapsed, the timer will stop.
|
||||
*
|
||||
* @param now Current time in milliseconds (typically from millis())
|
||||
* @return true if the timer is still running, false if stopped or elapsed
|
||||
*/
|
||||
bool update(uint32_t now) {
|
||||
if (!running) {
|
||||
return false;
|
||||
}
|
||||
uint32_t elapsed = now - start_time;
|
||||
if (elapsed > duration) {
|
||||
running = false;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t start_time; // When the timer was started (in milliseconds)
|
||||
uint32_t duration; // How long the timer should run (in milliseconds)
|
||||
bool running; // Whether the timer is currently active
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
/// @file FxPacifica.ino
|
||||
/// @brief Pacifica ocean effect with ScreenMap
|
||||
/// @example FxPacifica.ino
|
||||
///
|
||||
/// 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`
|
||||
/// 4. When the compiler is done a web page will open.
|
||||
|
||||
//
|
||||
// "Pacifica"
|
||||
// Gentle, blue-green ocean waves.
|
||||
// December 2019, Mark Kriegsman and Mary Corey March.
|
||||
// For Dan.
|
||||
//
|
||||
|
||||
|
||||
#define FASTLED_ALLOW_INTERRUPTS 0
|
||||
#include <FastLED.h>
|
||||
#include "fx/1d/pacifica.h"
|
||||
#include "fl/screenmap.h"
|
||||
#include "defs.h" // for ENABLE_SKETCH
|
||||
|
||||
#if !ENABLE_SKETCH
|
||||
void setup() {}
|
||||
void loop() {}
|
||||
#else
|
||||
|
||||
|
||||
using namespace fl;
|
||||
|
||||
#define DATA_PIN 3
|
||||
#define NUM_LEDS 60
|
||||
#define MAX_POWER_MILLIAMPS 500
|
||||
#define LED_TYPE WS2812B
|
||||
#define COLOR_ORDER GRB
|
||||
|
||||
CRGB leds[NUM_LEDS];
|
||||
Pacifica pacifica(NUM_LEDS);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
ScreenMap screenMap = ScreenMap::DefaultStrip(NUM_LEDS, 1.5f, 0.5f);
|
||||
FastLED.addLeds<LED_TYPE,DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS)
|
||||
.setCorrection(TypicalLEDStrip)
|
||||
.setScreenMap(screenMap);
|
||||
FastLED.setMaxPowerInVoltsAndMilliamps(5, MAX_POWER_MILLIAMPS);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
EVERY_N_MILLISECONDS(20) {
|
||||
pacifica.draw(Fx::DrawContext(millis(), leds));
|
||||
FastLED.show();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ENABLE_SKETCH
|
||||
9
.pio/libdeps/esp01_1m/FastLED/examples/FxPacifica/defs.h
Normal file
9
.pio/libdeps/esp01_1m/FastLED/examples/FxPacifica/defs.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
#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)
|
||||
#define ENABLE_SKETCH 0
|
||||
#else
|
||||
#define ENABLE_SKETCH 1
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
/// @file FxPride2015.ino
|
||||
/// @brief Pride2015 effect with ScreenMap
|
||||
/// @example FxPride2015.ino
|
||||
///
|
||||
/// 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`
|
||||
/// 4. When the compiler is done a web page will open.
|
||||
|
||||
#include <FastLED.h>
|
||||
#include "fx/1d/pride2015.h"
|
||||
#include "fl/screenmap.h"
|
||||
|
||||
using namespace fl;
|
||||
|
||||
#define DATA_PIN 3
|
||||
#define LED_TYPE WS2811
|
||||
#define COLOR_ORDER GRB
|
||||
#define NUM_LEDS 200
|
||||
#define BRIGHTNESS 255
|
||||
|
||||
CRGB leds[NUM_LEDS];
|
||||
Pride2015 pride(NUM_LEDS);
|
||||
|
||||
void setup() {
|
||||
ScreenMap screenMap = ScreenMap::DefaultStrip(NUM_LEDS, 1.5f, 0.8f);
|
||||
|
||||
// tell FastLED about the LED strip configuration
|
||||
FastLED.addLeds<LED_TYPE,DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS)
|
||||
.setCorrection(TypicalLEDStrip)
|
||||
.setScreenMap(screenMap)
|
||||
.setDither(BRIGHTNESS < 255);
|
||||
|
||||
// set master brightness control
|
||||
FastLED.setBrightness(BRIGHTNESS);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
pride.draw(Fx::DrawContext(millis(), leds));
|
||||
FastLED.show();
|
||||
}
|
||||
136
.pio/libdeps/esp01_1m/FastLED/examples/FxSdCard/FxSdCard.ino
Normal file
136
.pio/libdeps/esp01_1m/FastLED/examples/FxSdCard/FxSdCard.ino
Normal file
@@ -0,0 +1,136 @@
|
||||
/// @file SdCard.ino
|
||||
/// @brief Demonstrates playing a video on FastLED.
|
||||
/// @author Zach Vorhies
|
||||
///
|
||||
/// 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`
|
||||
/// 4. When the compiler is done a web page will open.
|
||||
|
||||
#include "FastLED.h"
|
||||
|
||||
#if !SKETCH_HAS_LOTS_OF_MEMORY
|
||||
void setup() {
|
||||
// put your setup code here, to run once:
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// put your main code here, to run repeatedly:
|
||||
}
|
||||
#else
|
||||
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#include "fx/2d/noisepalette.h"
|
||||
// #include "fx/2d/animartrix.hpp"
|
||||
#include "fx/fx_engine.h"
|
||||
#include "fx/video.h"
|
||||
#include "fl/file_system.h"
|
||||
#include "fl/ui.h"
|
||||
#include "fl/screenmap.h"
|
||||
#include "fl/file_system.h"
|
||||
|
||||
|
||||
using namespace fl;
|
||||
|
||||
|
||||
#define LED_PIN 2
|
||||
#define LED_TYPE WS2811
|
||||
#define COLOR_ORDER GRB
|
||||
#define FPS 60
|
||||
#define CHIP_SELECT_PIN 5
|
||||
|
||||
|
||||
|
||||
#define MATRIX_WIDTH 32
|
||||
#define MATRIX_HEIGHT 32
|
||||
#define NUM_VIDEO_FRAMES 2 // enables interpolation with > 1 frame.
|
||||
|
||||
|
||||
#define NUM_LEDS (MATRIX_WIDTH * MATRIX_HEIGHT)
|
||||
#define IS_SERPINTINE true
|
||||
|
||||
|
||||
UITitle title("SDCard Demo - Mapped Video");
|
||||
UIDescription description("Video data is streamed off of a SD card and displayed on a LED strip. The video data is mapped to the LED strip using a ScreenMap.");
|
||||
|
||||
|
||||
CRGB leds[NUM_LEDS];
|
||||
ScreenMap screenMap;
|
||||
|
||||
FileSystem filesystem;
|
||||
Video video;
|
||||
Video video2;
|
||||
|
||||
UISlider videoSpeed("Video Speed", 1.0f, -1, 2.0f, 0.01f);
|
||||
UINumberField whichVideo("Which Video", 0, 0, 1);
|
||||
|
||||
|
||||
bool gError = false;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Sketch setup");
|
||||
// Initialize the file system and check for errors
|
||||
if (!filesystem.beginSd(CHIP_SELECT_PIN)) {
|
||||
Serial.println("Failed to initialize file system.");
|
||||
}
|
||||
|
||||
// Open video files from the SD card
|
||||
video = filesystem.openVideo("data/video.rgb", NUM_LEDS, FPS, 2);
|
||||
if (!video) {
|
||||
FASTLED_WARN("Failed to instantiate video");
|
||||
gError = true;
|
||||
return;
|
||||
}
|
||||
video2 = filesystem.openVideo("data/color_line_bubbles.rgb", NUM_LEDS, FPS, 2);
|
||||
if (!video2) {
|
||||
FASTLED_WARN("Failed to instantiate video2");
|
||||
gError = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Read the screen map configuration
|
||||
ScreenMap screenMap;
|
||||
bool ok = filesystem.readScreenMap("data/screenmap.json", "strip1", &screenMap);
|
||||
if (!ok) {
|
||||
Serial.println("Failed to read screen map");
|
||||
gError = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Configure FastLED with the LED type, pin, and color order
|
||||
FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS)
|
||||
.setCorrection(TypicalLEDStrip)
|
||||
.setScreenMap(screenMap);
|
||||
FastLED.setBrightness(96);
|
||||
Serial.println("FastLED setup done");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
static bool s_first = true;
|
||||
if (s_first) {
|
||||
s_first = false;
|
||||
Serial.println("First loop.");
|
||||
}
|
||||
if (gError) {
|
||||
// If an error occurred, print a warning every second
|
||||
EVERY_N_SECONDS(1) {
|
||||
FASTLED_WARN("No loop because an error occured.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Select the video to play based on the UI input
|
||||
Video& vid = !bool(whichVideo.value()) ? video : video2;
|
||||
vid.setTimeScale(videoSpeed);
|
||||
|
||||
// Get the current time and draw the video frame
|
||||
uint32_t now = millis();
|
||||
vid.draw(now, leds);
|
||||
FastLED.show();
|
||||
}
|
||||
|
||||
#endif
|
||||
Binary file not shown.
@@ -0,0 +1,4 @@
|
||||
data/ directory will appear automatically in emscripten web builds.
|
||||
|
||||
* The .rgb file represents uncompressed RGB video data.
|
||||
* the the screenmap.json is the screenmap for "strip1"
|
||||
File diff suppressed because one or more lines are too long
BIN
.pio/libdeps/esp01_1m/FastLED/examples/FxSdCard/data/video.rgb
Normal file
BIN
.pio/libdeps/esp01_1m/FastLED/examples/FxSdCard/data/video.rgb
Normal file
Binary file not shown.
@@ -0,0 +1,30 @@
|
||||
#include "FastLED.h"
|
||||
#include "fx/1d/twinklefox.h"
|
||||
|
||||
using namespace fl;
|
||||
|
||||
#define NUM_LEDS 100
|
||||
#define LED_TYPE WS2811
|
||||
#define COLOR_ORDER GRB
|
||||
#define DATA_PIN 3
|
||||
#define VOLTS 12
|
||||
#define MAX_MA 4000
|
||||
|
||||
CRGBArray<NUM_LEDS> leds;
|
||||
TwinkleFox twinkleFox(NUM_LEDS);
|
||||
|
||||
void setup() {
|
||||
delay(3000); // safety startup delay
|
||||
FastLED.setMaxPowerInVoltsAndMilliamps(VOLTS, MAX_MA);
|
||||
FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS)
|
||||
.setCorrection(TypicalLEDStrip)
|
||||
.setRgbw();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
EVERY_N_SECONDS(SECONDS_PER_PALETTE) {
|
||||
twinkleFox.chooseNextColorPalette(twinkleFox.targetPalette);
|
||||
}
|
||||
twinkleFox.draw(Fx::DrawContext(millis(), leds));
|
||||
FastLED.show();
|
||||
}
|
||||
159
.pio/libdeps/esp01_1m/FastLED/examples/FxWater/FxWater.h
Normal file
159
.pio/libdeps/esp01_1m/FastLED/examples/FxWater/FxWater.h
Normal file
@@ -0,0 +1,159 @@
|
||||
/// @file FxWater.ino
|
||||
/// @brief Water effect with XYMap
|
||||
/// @example FxWater.ino
|
||||
///
|
||||
/// 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`
|
||||
/// 4. When the compiler is done a web page will open.
|
||||
|
||||
// Author: sutaburosu
|
||||
|
||||
// based on https://web.archive.org/web/20160418004149/http://freespace.virgin.net/hugo.elias/graphics/x_water.htm
|
||||
|
||||
#include <FastLED.h>
|
||||
#include "Arduino.h"
|
||||
#include "fl/xymap.h"
|
||||
|
||||
using namespace fl;
|
||||
|
||||
#define WIDTH 32
|
||||
#define HEIGHT 32
|
||||
#define NUM_LEDS ((WIDTH) * (HEIGHT))
|
||||
CRGB leds[NUM_LEDS];
|
||||
|
||||
// the water needs 2 arrays each slightly bigger than the screen
|
||||
#define WATERWIDTH (WIDTH + 2)
|
||||
#define WATERHEIGHT (HEIGHT + 2)
|
||||
uint8_t water[2][WATERWIDTH * WATERHEIGHT];
|
||||
|
||||
void wu_water(uint8_t * const buf, uint16_t x, uint16_t y, uint8_t bright);
|
||||
void process_water(uint8_t * src, uint8_t * dst) ;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
FastLED.addLeds<NEOPIXEL, 2>(leds, NUM_LEDS).setScreenMap(WIDTH, HEIGHT);
|
||||
}
|
||||
|
||||
// from: https://github.com/FastLED/FastLED/pull/202
|
||||
CRGB MyColorFromPaletteExtended(const CRGBPalette16& pal, uint16_t index, uint8_t brightness, TBlendType blendType) {
|
||||
// Extract the four most significant bits of the index as a palette index.
|
||||
uint8_t index_4bit = (index >> 12);
|
||||
// Calculate the 8-bit offset from the palette index.
|
||||
uint8_t offset = (uint8_t)(index >> 4);
|
||||
// Get the palette entry from the 4-bit index
|
||||
const CRGB* entry = &(pal[0]) + index_4bit;
|
||||
uint8_t red1 = entry->red;
|
||||
uint8_t green1 = entry->green;
|
||||
uint8_t blue1 = entry->blue;
|
||||
|
||||
uint8_t blend = offset && (blendType != NOBLEND);
|
||||
if (blend) {
|
||||
if (index_4bit == 15) {
|
||||
entry = &(pal[0]);
|
||||
} else {
|
||||
entry++;
|
||||
}
|
||||
|
||||
// Calculate the scaling factor and scaled values for the lower palette value.
|
||||
uint8_t f1 = 255 - offset;
|
||||
red1 = scale8_LEAVING_R1_DIRTY(red1, f1);
|
||||
green1 = scale8_LEAVING_R1_DIRTY(green1, f1);
|
||||
blue1 = scale8_LEAVING_R1_DIRTY(blue1, f1);
|
||||
|
||||
// Calculate the scaled values for the neighbouring palette value.
|
||||
uint8_t red2 = entry->red;
|
||||
uint8_t green2 = entry->green;
|
||||
uint8_t blue2 = entry->blue;
|
||||
red2 = scale8_LEAVING_R1_DIRTY(red2, offset);
|
||||
green2 = scale8_LEAVING_R1_DIRTY(green2, offset);
|
||||
blue2 = scale8_LEAVING_R1_DIRTY(blue2, offset);
|
||||
cleanup_R1();
|
||||
|
||||
// These sums can't overflow, so no qadd8 needed.
|
||||
red1 += red2;
|
||||
green1 += green2;
|
||||
blue1 += blue2;
|
||||
}
|
||||
if (brightness != 255) {
|
||||
// nscale8x3_video(red1, green1, blue1, brightness);
|
||||
nscale8x3(red1, green1, blue1, brightness);
|
||||
}
|
||||
return CRGB(red1, green1, blue1);
|
||||
}
|
||||
|
||||
// Rectangular grid
|
||||
XYMap xyMap(WIDTH, HEIGHT, false);
|
||||
|
||||
// map X & Y coordinates onto a horizontal serpentine matrix layout
|
||||
uint16_t XY(uint8_t x, uint8_t y) {
|
||||
return xyMap.mapToIndex(x, y);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// swap the src/dest buffers on each frame
|
||||
static uint8_t buffer = 0;
|
||||
uint8_t * const bufA = &water[buffer][0];
|
||||
buffer = (buffer + 1) % 2;
|
||||
uint8_t * const bufB = &water[buffer][0];
|
||||
|
||||
// add a moving stimulus
|
||||
wu_water(bufA, beatsin16(13, 256, HEIGHT * 256), beatsin16(7, 256, WIDTH * 256), beatsin8(160, 64, 255));
|
||||
|
||||
// animate the water
|
||||
process_water(bufA, bufB);
|
||||
|
||||
|
||||
// display the water effect on the LEDs
|
||||
uint8_t * input = bufB + WATERWIDTH - 1;
|
||||
static uint16_t pal_offset = 0;
|
||||
pal_offset += 256;
|
||||
for (uint8_t y = 0; y < HEIGHT; y++) {
|
||||
input += 2;
|
||||
for (uint8_t x = 0; x < WIDTH; x++) {
|
||||
leds[XY(x, y)] = MyColorFromPaletteExtended(RainbowColors_p, pal_offset + (*input++ << 8), 255, LINEARBLEND);
|
||||
}
|
||||
}
|
||||
FastLED.show();
|
||||
}
|
||||
|
||||
void process_water(uint8_t * src, uint8_t * dst) {
|
||||
src += WATERWIDTH - 1;
|
||||
dst += WATERWIDTH - 1;
|
||||
for (uint8_t y = 1; y < WATERHEIGHT - 1; y++) {
|
||||
src += 2; dst += 2;
|
||||
for (uint8_t x = 1; x < WATERWIDTH - 1; x++) {
|
||||
uint16_t t = src[-1] + src[1] + src[-WATERWIDTH] + src[WATERWIDTH];
|
||||
t >>= 1;
|
||||
if (dst[0] < t)
|
||||
dst[0] = t - dst[0];
|
||||
else
|
||||
dst[0] = 0;
|
||||
|
||||
dst[0] -= dst[0] >> 6;
|
||||
src++; dst++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// draw a blob of 4 pixels with their relative brightnesses conveying sub-pixel positioning
|
||||
void wu_water(uint8_t * const buf, uint16_t x, uint16_t y, uint8_t bright) {
|
||||
// extract the fractional parts and derive their inverses
|
||||
uint8_t xx = x & 0xff, yy = y & 0xff, ix = 255 - xx, iy = 255 - yy;
|
||||
// calculate the intensities for each affected pixel
|
||||
#define WU_WEIGHT(a, b) ((uint8_t)(((a) * (b) + (a) + (b)) >> 8))
|
||||
uint8_t wu[4] = {WU_WEIGHT(ix, iy), WU_WEIGHT(xx, iy),
|
||||
WU_WEIGHT(ix, yy), WU_WEIGHT(xx, yy)
|
||||
};
|
||||
#undef WU_WEIGHT
|
||||
// multiply the intensities by the colour, and saturating-add them to the pixels
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
uint8_t local_x = (x >> 8) + (i & 1);
|
||||
uint8_t local_y = (y >> 8) + ((i >> 1) & 1);
|
||||
uint16_t xy = WATERWIDTH * local_y + local_x;
|
||||
if (xy >= WATERWIDTH * WATERHEIGHT) continue;
|
||||
uint16_t this_bright = bright * wu[i];
|
||||
buf[xy] = qadd8(buf[xy], this_bright >> 8);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#include "fl/sketch_macros.h"
|
||||
#if SKETCH_HAS_LOTS_OF_MEMORY
|
||||
#include "./FxWater.h"
|
||||
#else
|
||||
#include "platforms/sketch_fake.hpp"
|
||||
#endif
|
||||
46
.pio/libdeps/esp01_1m/FastLED/examples/FxWave2d/FxWave2d.ino
Normal file
46
.pio/libdeps/esp01_1m/FastLED/examples/FxWave2d/FxWave2d.ino
Normal file
@@ -0,0 +1,46 @@
|
||||
|
||||
|
||||
/*
|
||||
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 demonstrates a 2D wave simulation with multiple layers and blending effects.
|
||||
It creates ripple effects that propagate across the LED matrix, similar to water waves.
|
||||
The demo includes two wave layers (upper and lower) with different colors and properties,
|
||||
which are blended together to create complex visual effects.
|
||||
*/
|
||||
|
||||
#include <Arduino.h> // Core Arduino functionality
|
||||
#include <FastLED.h> // Main FastLED library for controlling LEDs
|
||||
#include "fl/sketch_macros.h"
|
||||
|
||||
#if !SKETCH_HAS_LOTS_OF_MEMORY
|
||||
// Platform does not have enough memory
|
||||
void setup() {}
|
||||
void loop() {}
|
||||
#else
|
||||
|
||||
#include "wavefx.h"
|
||||
|
||||
using namespace fl; // Use the FastLED namespace for convenience
|
||||
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200); // Initialize serial communication for debugging
|
||||
wavefx_setup();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// The main program loop that runs continuously
|
||||
wavefx_loop();
|
||||
}
|
||||
#endif
|
||||
445
.pio/libdeps/esp01_1m/FastLED/examples/FxWave2d/wavefx.cpp
Normal file
445
.pio/libdeps/esp01_1m/FastLED/examples/FxWave2d/wavefx.cpp
Normal file
@@ -0,0 +1,445 @@
|
||||
|
||||
/*
|
||||
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 demonstrates a 2D wave simulation with multiple layers and blending effects.
|
||||
It creates ripple effects that propagate across the LED matrix, similar to water waves.
|
||||
The demo includes two wave layers (upper and lower) with different colors and properties,
|
||||
which are blended together to create complex visual effects.
|
||||
*/
|
||||
|
||||
#include <Arduino.h> // Core Arduino functionality
|
||||
#include <FastLED.h> // Main FastLED library for controlling LEDs
|
||||
|
||||
#if SKETCH_HAS_LOTS_OF_MEMORY
|
||||
|
||||
#include "fl/math_macros.h" // Math helper functions and macros
|
||||
#include "fl/time_alpha.h" // Time-based alpha/transition effects
|
||||
#include "fl/ui.h" // UI components for the FastLED web compiler
|
||||
#include "fx/2d/blend.h" // 2D blending effects between layers
|
||||
#include "fx/2d/wave.h" // 2D wave simulation
|
||||
|
||||
#include "wavefx.h" // Header file for this sketch
|
||||
|
||||
using namespace fl; // Use the FastLED namespace for convenience
|
||||
|
||||
|
||||
|
||||
// Array to hold all LED color values - one CRGB struct per LED
|
||||
CRGB leds[NUM_LEDS];
|
||||
|
||||
// UI elements that appear in the FastLED web compiler interface:
|
||||
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
|
||||
UICheckbox autoTrigger("Auto Trigger", true); // Enable/disable automatic ripple triggering
|
||||
UISlider triggerSpeed("Trigger Speed", .5f, 0.0f, 1.0f, 0.01f); // Controls how frequently auto-triggers happen (lower = faster)
|
||||
UICheckbox easeModeSqrt("Ease Mode Sqrt", false); // Changes how wave heights are calculated (sqrt gives more natural waves)
|
||||
UICheckbox useChangeGrid("Use Change Grid", false); // Enable performance optimization (reduces visual oscillation)
|
||||
UISlider blurAmount("Global Blur Amount", 0, 0, 172, 1); // Controls overall blur amount for all layers
|
||||
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)
|
||||
|
||||
|
||||
|
||||
// Upper wave layer controls:
|
||||
UISlider speedUpper("Wave Upper: Speed", 0.12f, 0.0f, 1.0f); // How fast the upper wave propagates
|
||||
UISlider dampeningUpper("Wave Upper: Dampening", 8.9f, 0.0f, 20.0f, 0.1f); // How quickly the upper wave loses energy
|
||||
UICheckbox halfDuplexUpper("Wave Upper: Half Duplex", true); // If true, waves only go positive (not negative)
|
||||
UISlider blurAmountUpper("Wave Upper: Blur Amount", 95, 0, 172, 1); // Blur amount for upper wave layer
|
||||
UISlider blurPassesUpper("Wave Upper: Blur Passes", 1, 1, 10, 1); // Blur passes for upper wave layer
|
||||
|
||||
// Lower wave layer controls:
|
||||
UISlider speedLower("Wave Lower: Speed", 0.26f, 0.0f, 1.0f); // How fast the lower wave propagates
|
||||
UISlider dampeningLower("Wave Lower: Dampening", 9.0f, 0.0f, 20.0f, 0.1f); // How quickly the lower wave loses energy
|
||||
UICheckbox halfDuplexLower("Wave Lower: Half Duplex", true); // If true, waves only go positive (not negative)
|
||||
UISlider blurAmountLower("Wave Lower: Blur Amount", 0, 0, 172, 1); // Blur amount for lower wave layer
|
||||
UISlider blurPassesLower("Wave Lower: Blur Passes", 1, 1, 10, 1); // Blur passes for lower wave layer
|
||||
|
||||
// Fancy effect controls (for the cross-shaped effect):
|
||||
UISlider fancySpeed("Fancy Speed", 796, 0, 1000, 1); // Speed of the fancy effect animation
|
||||
UISlider fancyIntensity("Fancy Intensity", 32, 1, 255, 1); // Intensity/height of the fancy effect waves
|
||||
UISlider fancyParticleSpan("Fancy Particle Span", 0.06f, 0.01f, 0.2f, 0.01f); // Width of the fancy effect lines
|
||||
|
||||
// Help text explaining the Use Change Grid feature
|
||||
UIHelp changeGridHelp("Use Change Grid preserves the set point over multiple iterations to ensure more stable results across simulation resolutions. However, turning it off may result in more dramatic effects and saves memory.");
|
||||
|
||||
// Color palettes define the gradient of colors used for the wave effects
|
||||
// Each entry has the format: position (0-255), R, G, B
|
||||
|
||||
DEFINE_GRADIENT_PALETTE(electricBlueFirePal){
|
||||
0, 0, 0, 0, // Black (lowest wave height)
|
||||
32, 0, 0, 70, // Dark blue (low wave height)
|
||||
128, 20, 57, 255, // Electric blue (medium wave height)
|
||||
255, 255, 255, 255 // White (maximum wave height)
|
||||
};
|
||||
|
||||
DEFINE_GRADIENT_PALETTE(electricGreenFirePal){
|
||||
0, 0, 0, 0, // Black (lowest wave height)
|
||||
8, 128, 64, 64, // Green with red tint (very low wave height)
|
||||
16, 255, 222, 222, // Pinkish red (low wave height)
|
||||
64, 255, 255, 255, // White (medium wave height)
|
||||
255, 255, 255, 255 // White (maximum wave height)
|
||||
};
|
||||
|
||||
// Create mappings between 1D array positions and 2D x,y coordinates
|
||||
XYMap xyMap(WIDTH, HEIGHT, IS_SERPINTINE); // For the actual LED output (may be serpentine)
|
||||
XYMap xyRect(WIDTH, HEIGHT, false); // For the wave simulation (always rectangular grid)
|
||||
|
||||
// Create default configuration for the lower wave layer
|
||||
WaveFx::Args CreateArgsLower() {
|
||||
WaveFx::Args out;
|
||||
out.factor = SuperSample::SUPER_SAMPLE_2X; // 2x supersampling for smoother waves
|
||||
out.half_duplex = true; // Only positive waves (no negative values)
|
||||
out.auto_updates = true; // Automatically update the simulation each frame
|
||||
out.speed = 0.18f; // Wave propagation speed
|
||||
out.dampening = 9.0f; // How quickly waves lose energy
|
||||
out.crgbMap = fl::make_shared<WaveCrgbGradientMap>(electricBlueFirePal); // Color palette for this wave
|
||||
return out;
|
||||
}
|
||||
|
||||
// Create default configuration for the upper wave layer
|
||||
WaveFx::Args CreateArgsUpper() {
|
||||
WaveFx::Args out;
|
||||
out.factor = SuperSample::SUPER_SAMPLE_2X; // 2x supersampling for smoother waves
|
||||
out.half_duplex = true; // Only positive waves (no negative values)
|
||||
out.auto_updates = true; // Automatically update the simulation each frame
|
||||
out.speed = 0.25f; // Wave propagation speed (faster than lower)
|
||||
out.dampening = 3.0f; // How quickly waves lose energy (less than lower)
|
||||
out.crgbMap = fl::make_shared<WaveCrgbGradientMap>(electricGreenFirePal); // Color palette for this wave
|
||||
return out;
|
||||
}
|
||||
|
||||
// Create the two wave simulation layers with their default configurations
|
||||
WaveFx waveFxLower(xyRect, CreateArgsLower()); // Lower/background wave layer (blue)
|
||||
WaveFx waveFxUpper(xyRect, CreateArgsUpper()); // Upper/foreground wave layer (green/red)
|
||||
|
||||
// Create a blender that will combine the two wave layers
|
||||
Blend2d fxBlend(xyMap);
|
||||
|
||||
|
||||
// Convert the UI slider value to the appropriate SuperSample enum value
|
||||
// SuperSample controls the quality of the wave simulation (higher = better quality but more CPU)
|
||||
SuperSample getSuperSample() {
|
||||
switch (int(superSample)) {
|
||||
case 0:
|
||||
return SuperSample::SUPER_SAMPLE_NONE; // No supersampling (fastest, lowest quality)
|
||||
case 1:
|
||||
return SuperSample::SUPER_SAMPLE_2X; // 2x supersampling (2x2 grid = 4 samples per pixel)
|
||||
case 2:
|
||||
return SuperSample::SUPER_SAMPLE_4X; // 4x supersampling (4x4 grid = 16 samples per pixel)
|
||||
case 3:
|
||||
return SuperSample::SUPER_SAMPLE_8X; // 8x supersampling (8x8 grid = 64 samples per pixel, slowest)
|
||||
default:
|
||||
return SuperSample::SUPER_SAMPLE_NONE; // Default fallback
|
||||
}
|
||||
}
|
||||
|
||||
// Create a ripple effect at a random position within the central area of the display
|
||||
void triggerRipple() {
|
||||
// Define a margin percentage to keep ripples away from the edges
|
||||
float perc = .15f;
|
||||
|
||||
// Calculate the boundaries for the ripple (15% from each edge)
|
||||
uint8_t min_x = perc * WIDTH; // Left boundary
|
||||
uint8_t max_x = (1 - perc) * WIDTH; // Right boundary
|
||||
uint8_t min_y = perc * HEIGHT; // Top boundary
|
||||
uint8_t max_y = (1 - perc) * HEIGHT; // Bottom boundary
|
||||
|
||||
// Generate a random position within these boundaries
|
||||
int x = random(min_x, max_x);
|
||||
int y = random(min_y, max_y);
|
||||
|
||||
// Set a wave peak at this position in both wave layers
|
||||
// The value 1.0 represents the maximum height of the wave
|
||||
waveFxLower.setf(x, y, 1); // Create ripple in lower layer
|
||||
waveFxUpper.setf(x, y, 1); // Create ripple in upper layer
|
||||
}
|
||||
|
||||
// Create a fancy cross-shaped effect that expands from the center
|
||||
void applyFancyEffect(uint32_t now, bool button_active) {
|
||||
// Calculate the total animation duration based on the speed slider
|
||||
// Higher fancySpeed value = shorter duration (faster animation)
|
||||
uint32_t total =
|
||||
map(fancySpeed.as<uint32_t>(), 0, fancySpeed.getMax(), 1000, 100);
|
||||
|
||||
// Create a static TimeRamp to manage the animation timing
|
||||
// TimeRamp handles the transition from start to end over time
|
||||
static TimeRamp pointTransition = TimeRamp(total, 0, 0);
|
||||
|
||||
// If the button is active, start/restart the animation
|
||||
if (button_active) {
|
||||
pointTransition.trigger(now, total, 0, 0);
|
||||
}
|
||||
|
||||
// If the animation isn't currently active, exit early
|
||||
if (!pointTransition.isActive(now)) {
|
||||
// no need to draw
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the center of the display
|
||||
int mid_x = WIDTH / 2;
|
||||
int mid_y = HEIGHT / 2;
|
||||
|
||||
// Calculate the maximum distance from center (half the width)
|
||||
int amount = WIDTH / 2;
|
||||
|
||||
// Calculate the start and end coordinates for the cross
|
||||
int start_x = mid_x - amount; // Leftmost point
|
||||
int end_x = mid_x + amount; // Rightmost point
|
||||
int start_y = mid_y - amount; // Topmost point
|
||||
int end_y = mid_y + amount; // Bottommost point
|
||||
|
||||
// Get the current animation progress (0-255)
|
||||
int curr_alpha = pointTransition.update8(now);
|
||||
|
||||
// Map the animation progress to the four points of the expanding cross
|
||||
// As curr_alpha increases from 0 to 255, these points move from center to edges
|
||||
int left_x = map(curr_alpha, 0, 255, mid_x, start_x); // Center to left
|
||||
int down_y = map(curr_alpha, 0, 255, mid_y, start_y); // Center to top
|
||||
int right_x = map(curr_alpha, 0, 255, mid_x, end_x); // Center to right
|
||||
int up_y = map(curr_alpha, 0, 255, mid_y, end_y); // Center to bottom
|
||||
|
||||
// Convert the 0-255 alpha to 0.0-1.0 range
|
||||
float curr_alpha_f = curr_alpha / 255.0f;
|
||||
|
||||
// Calculate the wave height value - starts high and decreases as animation progresses
|
||||
// This makes the waves stronger at the beginning of the animation
|
||||
float valuef = (1.0f - curr_alpha_f) * fancyIntensity.value() / 255.0f;
|
||||
|
||||
// Calculate the width of the cross lines
|
||||
int span = fancyParticleSpan.value() * WIDTH;
|
||||
|
||||
// Add wave energy along the four expanding lines of the cross
|
||||
// Each line is a horizontal or vertical span of pixels
|
||||
|
||||
// Left-moving horizontal line
|
||||
for (int x = left_x - span; x < left_x + span; x++) {
|
||||
waveFxLower.addf(x, mid_y, valuef); // Add to lower layer
|
||||
waveFxUpper.addf(x, mid_y, valuef); // Add to upper layer
|
||||
}
|
||||
|
||||
// Right-moving horizontal line
|
||||
for (int x = right_x - span; x < right_x + span; x++) {
|
||||
waveFxLower.addf(x, mid_y, valuef);
|
||||
waveFxUpper.addf(x, mid_y, valuef);
|
||||
}
|
||||
|
||||
// Downward-moving vertical line
|
||||
for (int y = down_y - span; y < down_y + span; y++) {
|
||||
waveFxLower.addf(mid_x, y, valuef);
|
||||
waveFxUpper.addf(mid_x, y, valuef);
|
||||
}
|
||||
|
||||
// Upward-moving vertical line
|
||||
for (int y = up_y - span; y < up_y + span; y++) {
|
||||
waveFxLower.addf(mid_x, y, valuef);
|
||||
waveFxUpper.addf(mid_x, y, valuef);
|
||||
}
|
||||
}
|
||||
|
||||
// Structure to hold the state of UI buttons
|
||||
struct ui_state {
|
||||
bool button = false; // Regular ripple button state
|
||||
bool bigButton = false; // Fancy effect button state
|
||||
};
|
||||
|
||||
// Apply all UI settings to the wave effects and return button states
|
||||
ui_state ui() {
|
||||
// Set the easing function based on the checkbox
|
||||
// Easing controls how wave heights are calculated:
|
||||
// - LINEAR: Simple linear mapping (sharper waves)
|
||||
// - SQRT: Square root mapping (more natural, rounded waves)
|
||||
U8EasingFunction easeMode = easeModeSqrt
|
||||
? U8EasingFunction::WAVE_U8_MODE_SQRT
|
||||
: U8EasingFunction::WAVE_U8_MODE_LINEAR;
|
||||
|
||||
// Apply all settings from UI controls to the lower wave layer
|
||||
waveFxLower.setSpeed(speedLower); // Wave propagation speed
|
||||
waveFxLower.setDampening(dampeningLower); // How quickly waves lose energy
|
||||
waveFxLower.setHalfDuplex(halfDuplexLower); // Whether waves can go negative
|
||||
waveFxLower.setSuperSample(getSuperSample()); // Anti-aliasing quality
|
||||
waveFxLower.setEasingMode(easeMode); // Wave height calculation method
|
||||
waveFxLower.setUseChangeGrid(useChangeGrid); // Performance optimization vs visual quality
|
||||
|
||||
// Apply all settings from UI controls to the upper wave layer
|
||||
waveFxUpper.setSpeed(speedUpper); // Wave propagation speed
|
||||
waveFxUpper.setDampening(dampeningUpper); // How quickly waves lose energy
|
||||
waveFxUpper.setHalfDuplex(halfDuplexUpper); // Whether waves can go negative
|
||||
waveFxUpper.setSuperSample(getSuperSample()); // Anti-aliasing quality
|
||||
waveFxUpper.setEasingMode(easeMode); // Wave height calculation method
|
||||
waveFxUpper.setUseChangeGrid(useChangeGrid); // Performance optimization vs visual quality
|
||||
|
||||
// Apply global blur settings to the blender
|
||||
fxBlend.setGlobalBlurAmount(blurAmount); // Overall blur strength
|
||||
fxBlend.setGlobalBlurPasses(blurPasses); // Number of blur passes
|
||||
|
||||
// Create parameter structures for each wave layer's blur settings
|
||||
fl::Blend2dParams lower_params;
|
||||
lower_params.blur_amount = blurAmountLower; // Blur amount for lower layer
|
||||
lower_params.blur_passes = blurPassesLower; // Blur passes for lower layer
|
||||
|
||||
fl::Blend2dParams upper_params;
|
||||
upper_params.blur_amount = blurAmountUpper; // Blur amount for upper layer
|
||||
upper_params.blur_passes = blurPassesUpper; // Blur passes for upper layer
|
||||
|
||||
// Apply the layer-specific blur parameters
|
||||
fxBlend.setParams(waveFxLower, lower_params);
|
||||
fxBlend.setParams(waveFxUpper, upper_params);
|
||||
|
||||
// Return the current state of the UI buttons
|
||||
ui_state state;
|
||||
state.button = button; // Regular ripple button
|
||||
state.bigButton = buttonFancy; // Fancy effect button
|
||||
return state;
|
||||
}
|
||||
|
||||
// Handle automatic triggering of ripples at random intervals
|
||||
void processAutoTrigger(uint32_t now) {
|
||||
// Static variable to remember when the next auto-trigger should happen
|
||||
static uint32_t nextTrigger = 0;
|
||||
|
||||
// Calculate time until next trigger
|
||||
uint32_t trigger_delta = nextTrigger - now;
|
||||
|
||||
// Handle timer overflow (happens after ~49 days of continuous running)
|
||||
if (trigger_delta > 10000) {
|
||||
// If the delta is suspiciously large, we probably rolled over
|
||||
trigger_delta = 0;
|
||||
}
|
||||
|
||||
// Only proceed if auto-trigger is enabled
|
||||
if (autoTrigger) {
|
||||
// Check if it's time for the next trigger
|
||||
if (now >= nextTrigger) {
|
||||
// Create a ripple
|
||||
triggerRipple();
|
||||
|
||||
// Calculate the next trigger time based on the speed slider
|
||||
// Invert the speed value so higher slider = faster triggers
|
||||
float speed = 1.0f - triggerSpeed.value();
|
||||
|
||||
// Calculate min and max random intervals
|
||||
// Higher speed = shorter intervals between triggers
|
||||
uint32_t min_rand = 400 * speed; // Minimum interval (milliseconds)
|
||||
uint32_t max_rand = 2000 * speed; // Maximum interval (milliseconds)
|
||||
|
||||
// Ensure min is actually less than max (handles edge cases)
|
||||
uint32_t min = MIN(min_rand, max_rand);
|
||||
uint32_t max = MAX(min_rand, max_rand);
|
||||
|
||||
// Ensure min and max aren't equal (would cause random() to crash)
|
||||
if (min == max) {
|
||||
max += 1;
|
||||
}
|
||||
|
||||
// Schedule the next trigger at a random time in the future
|
||||
nextTrigger = now + random(min, max);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wavefx_setup() {
|
||||
// Create a screen map for visualization in the FastLED web compiler
|
||||
auto screenmap = xyMap.toScreenMap();
|
||||
screenmap.setDiameter(.2); // Set the size of the LEDs in the visualization
|
||||
|
||||
// Initialize the LED strip:
|
||||
// - NEOPIXEL is the LED type
|
||||
// - 2 is the data pin number (for real hardware)
|
||||
// - setScreenMap connects our 2D coordinate system to the 1D LED array
|
||||
FastLED.addLeds<NEOPIXEL, 2>(leds, NUM_LEDS).setScreenMap(screenmap);
|
||||
|
||||
// Set up UI groupings
|
||||
// Main Controls
|
||||
title.setGroup("Main Controls");
|
||||
description.setGroup("Main Controls");
|
||||
button.setGroup("Main Controls");
|
||||
buttonFancy.setGroup("Main Controls");
|
||||
autoTrigger.setGroup("Main Controls");
|
||||
triggerSpeed.setGroup("Main Controls");
|
||||
|
||||
// Global Settings
|
||||
xCyclical.setGroup("Global Settings");
|
||||
easeModeSqrt.setGroup("Global Settings");
|
||||
useChangeGrid.setGroup("Global Settings");
|
||||
blurAmount.setGroup("Global Settings");
|
||||
blurPasses.setGroup("Global Settings");
|
||||
superSample.setGroup("Global Settings");
|
||||
|
||||
// Upper Wave Layer
|
||||
speedUpper.setGroup("Upper Wave Layer");
|
||||
dampeningUpper.setGroup("Upper Wave Layer");
|
||||
halfDuplexUpper.setGroup("Upper Wave Layer");
|
||||
blurAmountUpper.setGroup("Upper Wave Layer");
|
||||
blurPassesUpper.setGroup("Upper Wave Layer");
|
||||
|
||||
// Lower Wave Layer
|
||||
speedLower.setGroup("Lower Wave Layer");
|
||||
dampeningLower.setGroup("Lower Wave Layer");
|
||||
halfDuplexLower.setGroup("Lower Wave Layer");
|
||||
blurAmountLower.setGroup("Lower Wave Layer");
|
||||
blurPassesLower.setGroup("Lower Wave Layer");
|
||||
|
||||
// Fancy Effects
|
||||
fancySpeed.setGroup("Fancy Effects");
|
||||
fancyIntensity.setGroup("Fancy Effects");
|
||||
fancyParticleSpan.setGroup("Fancy Effects");
|
||||
|
||||
// Add both wave layers to the blender
|
||||
// The order matters - lower layer is added first (background)
|
||||
fxBlend.add(waveFxLower);
|
||||
fxBlend.add(waveFxUpper);
|
||||
}
|
||||
|
||||
|
||||
void wavefx_loop() {
|
||||
// The main program loop that runs continuously
|
||||
|
||||
// Get the current time in milliseconds
|
||||
uint32_t now = millis();
|
||||
|
||||
// set the x cyclical
|
||||
waveFxLower.setXCylindrical(xCyclical.value()); // Set whether lower wave wraps around x-axis
|
||||
|
||||
// Apply all UI settings and get button states
|
||||
ui_state state = ui();
|
||||
|
||||
// Check if the regular ripple button was pressed
|
||||
if (state.button) {
|
||||
triggerRipple(); // Create a single ripple
|
||||
}
|
||||
|
||||
// Apply the fancy cross effect if its button is pressed
|
||||
applyFancyEffect(now, state.bigButton);
|
||||
|
||||
// Handle automatic triggering of ripples
|
||||
processAutoTrigger(now);
|
||||
|
||||
// Create a drawing context with the current time and LED array
|
||||
Fx::DrawContext ctx(now, leds);
|
||||
|
||||
// Draw the blended result of both wave layers to the LED array
|
||||
fxBlend.draw(ctx);
|
||||
|
||||
// Send the color data to the actual LEDs
|
||||
FastLED.show();
|
||||
}
|
||||
|
||||
|
||||
#endif // SKETCH_HAS_LOTS_OF_MEMORY
|
||||
33
.pio/libdeps/esp01_1m/FastLED/examples/FxWave2d/wavefx.h
Normal file
33
.pio/libdeps/esp01_1m/FastLED/examples/FxWave2d/wavefx.h
Normal file
@@ -0,0 +1,33 @@
|
||||
|
||||
#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
|
||||
all the UI elements you see below.
|
||||
|
||||
OVERVIEW:
|
||||
This sketch demonstrates a 2D wave simulation with multiple layers and blending effects.
|
||||
It creates ripple effects that propagate across the LED matrix, similar to water waves.
|
||||
The demo includes two wave layers (upper and lower) with different colors and properties,
|
||||
which are blended together to create complex visual effects.
|
||||
*/
|
||||
|
||||
|
||||
// Define the dimensions of our LED matrix
|
||||
#define HEIGHT 64 // Number of rows in the matrix
|
||||
#define WIDTH 64 // Number of columns in the matrix
|
||||
#define NUM_LEDS ((WIDTH) * (HEIGHT)) // Total number of LEDs
|
||||
#define IS_SERPINTINE true // Whether the LED strip zigzags back and forth (common in matrix layouts)
|
||||
|
||||
void wavefx_setup();
|
||||
void wavefx_loop();
|
||||
63
.pio/libdeps/esp01_1m/FastLED/examples/HD107/HD107.ino
Normal file
63
.pio/libdeps/esp01_1m/FastLED/examples/HD107/HD107.ino
Normal file
@@ -0,0 +1,63 @@
|
||||
/// @file HD107.ino
|
||||
/// @brief Example showing how to use the HD107 and HD which has built in gamma correction.
|
||||
/// This simply the HD107HD examles but with this chipsets.
|
||||
/// @see HD107HD.ino.
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <FastLED.h>
|
||||
#include <lib8tion.h>
|
||||
|
||||
#define NUM_LEDS 20
|
||||
// uint8_t DATA_PIN, uint8_t CLOCK_PIN,
|
||||
|
||||
#define STRIP_0_DATA_PIN 1
|
||||
#define STRIP_0_CLOCK_PIN 2
|
||||
#define STRIP_1_DATA_PIN 3
|
||||
#define STRIP_1_CLOCK_PIN 4
|
||||
|
||||
|
||||
CRGB leds_hd[NUM_LEDS] = {0}; // HD mode implies gamma.
|
||||
CRGB leds[NUM_LEDS] = {0}; // Software gamma mode.
|
||||
|
||||
// This is the regular gamma correction function that we used to have
|
||||
// to do. It's used here to showcase the difference between HD107HD
|
||||
// mode which does the gamma correction for you.
|
||||
CRGB software_gamma(const CRGB& in) {
|
||||
CRGB out;
|
||||
// dim8_raw are the old gamma correction functions.
|
||||
out.r = dim8_raw(in.r);
|
||||
out.g = dim8_raw(in.g);
|
||||
out.b = dim8_raw(in.b);
|
||||
return out;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
delay(500); // power-up safety delay
|
||||
// Two strips of LEDs, one in HD mode, one in software gamma mode.
|
||||
FastLED.addLeds<HD107HD, STRIP_0_DATA_PIN, STRIP_0_CLOCK_PIN, RGB>(leds_hd, NUM_LEDS);
|
||||
FastLED.addLeds<HD107, STRIP_1_DATA_PIN, STRIP_1_CLOCK_PIN, RGB>(leds, NUM_LEDS);
|
||||
}
|
||||
|
||||
uint8_t wrap_8bit(int i) {
|
||||
// Module % operator here wraps a large "i" so that it is
|
||||
// always in [0, 255] range when returned. For example, if
|
||||
// "i" is 256, then this will return 0. If "i" is 257
|
||||
// then this will return 1. No matter how big the "i" is, the
|
||||
// output range will always be [0, 255]
|
||||
return i % 256;
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Draw a a linear ramp of brightnesses to showcase the difference between
|
||||
// the HD and non-HD mode.
|
||||
for (int i = 0; i < NUM_LEDS; i++) {
|
||||
uint8_t brightness = map(i, 0, NUM_LEDS - 1, 0, 255);
|
||||
CRGB c(brightness, brightness, brightness); // Just make a shade of white.
|
||||
leds_hd[i] = c; // The HD107HD leds do their own gamma correction.
|
||||
CRGB c_gamma_corrected = software_gamma(c);
|
||||
leds[i] = c_gamma_corrected; // Set the software gamma corrected
|
||||
// values to the other strip.
|
||||
}
|
||||
FastLED.show(); // All leds are now written out.
|
||||
delay(8); // Wait 8 milliseconds until the next frame.
|
||||
}
|
||||
62
.pio/libdeps/esp01_1m/FastLED/examples/HSVTest/HSVTest.h
Normal file
62
.pio/libdeps/esp01_1m/FastLED/examples/HSVTest/HSVTest.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/// @file HSVTest.ino
|
||||
/// @brief Test HSV color space conversions
|
||||
/// @example HSVTest.ino
|
||||
///
|
||||
/// 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`
|
||||
/// 4. When the compiler is done a web page will open.
|
||||
|
||||
|
||||
#include "FastLED.h"
|
||||
|
||||
#define NUM_LEDS 5
|
||||
|
||||
#ifndef PIN_DATA
|
||||
#define PIN_DATA 1 // Universally available pin
|
||||
#endif
|
||||
|
||||
#ifndef PIN_CLOCK
|
||||
#define PIN_CLOCK 2 // Universally available pin
|
||||
#endif
|
||||
|
||||
CRGB leds[NUM_LEDS];
|
||||
|
||||
UISlider hueSlider("Hue", 128, 0, 255, 1);
|
||||
UISlider saturationSlider("Saturation", 255, 0, 255, 1);
|
||||
UISlider valueSlider("Value", 255, 0, 255, 1);
|
||||
UICheckbox autoHue("Auto Hue", true);
|
||||
|
||||
// Group related HSV control UI elements using UIGroup template multi-argument constructor
|
||||
UIGroup hsvControls("HSV Controls", hueSlider, saturationSlider, valueSlider, autoHue);
|
||||
|
||||
void setup() {
|
||||
//FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
|
||||
// fl::ScreenMap screenMap(NUM_LEDS);
|
||||
|
||||
FastLED.addLeds<APA102HD, PIN_DATA, PIN_CLOCK, BGR>(leds, NUM_LEDS)
|
||||
.setCorrection(TypicalLEDStrip);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
uint8_t hue = hueSlider.value();
|
||||
uint8_t saturation = saturationSlider.value();
|
||||
uint8_t value = valueSlider.value();
|
||||
|
||||
if (autoHue.value()) {
|
||||
uint32_t now = millis();
|
||||
uint32_t hue_offset = (now / 100) % 256;
|
||||
hue = hue_offset;
|
||||
hueSlider.setValue(hue);
|
||||
}
|
||||
|
||||
CHSV hsv(hue, saturation, value);
|
||||
|
||||
leds[0] = hsv2rgb_spectrum(hsv);
|
||||
leds[2] = hsv2rgb_rainbow(hsv);
|
||||
leds[4] = hsv2rgb_fullspectrum(hsv);
|
||||
|
||||
FastLED.show();
|
||||
}
|
||||
18
.pio/libdeps/esp01_1m/FastLED/examples/HSVTest/HSVTest.ino
Normal file
18
.pio/libdeps/esp01_1m/FastLED/examples/HSVTest/HSVTest.ino
Normal file
@@ -0,0 +1,18 @@
|
||||
#include "fl/sketch_macros.h"
|
||||
|
||||
#if SKETCH_HAS_LOTS_OF_MEMORY
|
||||
|
||||
#include "./HSVTest.h"
|
||||
|
||||
#else
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.println("Not enough memory");
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
#endif
|
||||
7
.pio/libdeps/esp01_1m/FastLED/examples/Json/Json.ino
Normal file
7
.pio/libdeps/esp01_1m/FastLED/examples/Json/Json.ino
Normal file
@@ -0,0 +1,7 @@
|
||||
#include "fl/sketch_macros.h"
|
||||
#if SKETCH_HAS_LOTS_OF_MEMORY
|
||||
#include "JsonSketch.h"
|
||||
#else
|
||||
void setup() {}
|
||||
void loop() {}
|
||||
#endif // SKETCH_HAS_LOTS_OF_MEMORY
|
||||
147
.pio/libdeps/esp01_1m/FastLED/examples/Json/JsonSketch.h
Normal file
147
.pio/libdeps/esp01_1m/FastLED/examples/Json/JsonSketch.h
Normal file
@@ -0,0 +1,147 @@
|
||||
/// @file JsonIdeal.ino
|
||||
/// @brief Demonstrates an ideal fluid JSON API with FastLED
|
||||
///
|
||||
/// This example showcases the proposed ideal JSON API for FastLED,
|
||||
/// emphasizing clean syntax, type safety, default values, and robust
|
||||
/// handling of missing fields.
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "FastLED.h"
|
||||
#include "fl/json.h" // Assumed ideal JSON API header
|
||||
|
||||
#define NUM_LEDS 100
|
||||
#define DATA_PIN 3
|
||||
|
||||
CRGB leds[NUM_LEDS];
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Initialize FastLED
|
||||
FastLED.addLeds<WS2812, DATA_PIN, GRB>(leds, NUM_LEDS);
|
||||
FastLED.setBrightness(64);
|
||||
|
||||
Serial.println("FastLED Ideal JSON API Demo Starting...");
|
||||
|
||||
// Example JSON string with LED configuration
|
||||
const char* configJson = R"({
|
||||
"strip": {
|
||||
"num_leds": 150,
|
||||
"pin": 5,
|
||||
"type": "WS2812B",
|
||||
"brightness": 200
|
||||
},
|
||||
"effects": {
|
||||
"current": "rainbow",
|
||||
"speed": 75
|
||||
},
|
||||
"animation_settings": {
|
||||
"duration_ms": 5000,
|
||||
"loop": true
|
||||
}
|
||||
})";
|
||||
|
||||
// NEW: Parse using ideal API
|
||||
fl::Json json = fl::Json::parse(configJson);
|
||||
|
||||
if (json.has_value()) {
|
||||
Serial.println("JSON parsed successfully with ideal API!");
|
||||
|
||||
// NEW: Clean syntax with default values - no more verbose error checking!
|
||||
int numLeds = json["strip"]["num_leds"] | 100; // Gets 150, or 100 if missing
|
||||
int pin = json["strip"]["pin"] | 3; // Gets 5, or 3 if missing
|
||||
fl::string type = json["strip"]["type"] | fl::string("WS2812"); // Gets "WS2812B"
|
||||
int brightness = json["strip"]["brightness"] | 64; // Gets 200, or 64 if missing
|
||||
|
||||
// Safe access to missing values - no crashes!
|
||||
int missing = json["non_existent"]["missing"] | 999; // Gets 999
|
||||
|
||||
Serial.println("LED Strip Configuration:");
|
||||
Serial.print(" LEDs: "); Serial.println(numLeds);
|
||||
Serial.print(" Pin: "); Serial.println(pin);
|
||||
Serial.print(" Type: "); Serial.println(type.c_str());
|
||||
Serial.print(" Brightness: "); Serial.println(brightness);
|
||||
Serial.print(" Missing field default: "); Serial.println(missing);
|
||||
|
||||
// Effect configuration with safe defaults
|
||||
fl::string effect = json["effects"]["current"] | fl::string("solid");
|
||||
int speed = json["effects"]["speed"] | 50;
|
||||
|
||||
Serial.println("Effect Configuration:");
|
||||
Serial.print(" Current: "); Serial.println(effect.c_str());
|
||||
Serial.print(" Speed: "); Serial.println(speed);
|
||||
|
||||
// Accessing nested objects with defaults
|
||||
long duration = json["animation_settings"]["duration_ms"] | 1000;
|
||||
bool loop = json["animation_settings"]["loop"] | false;
|
||||
|
||||
Serial.println("Animation Settings:");
|
||||
Serial.print(" Duration (ms): "); Serial.println(static_cast<int32_t>(duration));
|
||||
Serial.print(" Loop: "); Serial.println(loop ? "true" : "false");
|
||||
|
||||
Serial.println("\n=== NEW ERGONOMIC API DEMONSTRATION ===");
|
||||
|
||||
// Example JSON with mixed data types including strings that can be converted
|
||||
const char* mixedJson = R"({
|
||||
"config": {
|
||||
"brightness": "128",
|
||||
"timeout": "5.5",
|
||||
"enabled": true,
|
||||
"name": "LED Strip"
|
||||
}
|
||||
})";
|
||||
|
||||
fl::Json config = fl::Json::parse(mixedJson);
|
||||
|
||||
Serial.println("\nThree New Conversion Methods:");
|
||||
|
||||
// Method 1: try_as<T>() - Explicit optional handling
|
||||
Serial.println("\n1. try_as<T>() - When you need explicit error handling:");
|
||||
auto maybeBrightness = config["config"]["brightness"].try_as<int>();
|
||||
if (maybeBrightness.has_value()) {
|
||||
Serial.print(" Brightness converted from string: ");
|
||||
Serial.println(*maybeBrightness);
|
||||
} else {
|
||||
Serial.println(" Brightness conversion failed");
|
||||
}
|
||||
|
||||
// Method 2: value<T>() - Direct conversion with sensible defaults
|
||||
Serial.println("\n2. value<T>() - When you want defaults and don't care about failure:");
|
||||
int brightnessDirect = config["config"]["brightness"].value<int>();
|
||||
int missingDirect = config["missing_field"].value<int>();
|
||||
Serial.print(" Brightness (from string): ");
|
||||
Serial.println(brightnessDirect);
|
||||
Serial.print(" Missing field (default 0): ");
|
||||
Serial.println(missingDirect);
|
||||
|
||||
// Method 3: as_or<T>(default) - Custom defaults
|
||||
Serial.println("\n3. as_or<T>(default) - When you want custom defaults:");
|
||||
int customBrightness = config["config"]["brightness"].as_or<int>(255);
|
||||
int customMissing = config["missing_field"].as_or<int>(100);
|
||||
double timeout = config["config"]["timeout"].as_or<double>(10.0);
|
||||
Serial.print(" Brightness with custom default: ");
|
||||
Serial.println(customBrightness);
|
||||
Serial.print(" Missing with custom default: ");
|
||||
Serial.println(customMissing);
|
||||
Serial.print(" Timeout (string to double): ");
|
||||
Serial.println(timeout);
|
||||
|
||||
Serial.println("\nNew API provides:");
|
||||
Serial.println(" ✓ Type safety with automatic string-to-number conversion");
|
||||
Serial.println(" ✓ Three distinct patterns for different use cases");
|
||||
Serial.println(" ✓ Backward compatibility with existing as<T>() API");
|
||||
Serial.println(" ✓ Clean, readable syntax");
|
||||
Serial.println(" ✓ Significantly less code for common operations");
|
||||
|
||||
} else {
|
||||
Serial.println("JSON parsing failed with ideal API");
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Simple LED pattern to show something is happening
|
||||
static uint8_t hue = 0;
|
||||
fill_rainbow(leds, NUM_LEDS, hue++, 7);
|
||||
FastLED.show();
|
||||
delay(50);
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
/// This is a work in progress showcasing a complex MIDI keyboard
|
||||
/// visualizer.
|
||||
/// To run this compiler use
|
||||
/// > pip install fastled
|
||||
/// Then go to your sketch directory and run
|
||||
/// > fastled
|
||||
|
||||
#include "shared/defs.h"
|
||||
|
||||
#if !ENABLE_SKETCH
|
||||
// avr can't compile this, neither can the esp8266.
|
||||
void setup() {}
|
||||
void loop() {}
|
||||
#else
|
||||
|
||||
|
||||
//#define DEBUG_PAINTER
|
||||
//#define DEBUG_KEYBOARD 1
|
||||
|
||||
// Repeated keyboard presses in the main loop
|
||||
#define DEBUG_FORCED_KEYBOARD
|
||||
// #define DEBUG_MIDI_KEY 72
|
||||
|
||||
#define MIDI_SERIAL_PORT Serial1
|
||||
|
||||
#define FASTLED_UI // Brings in the UI components.
|
||||
#include "FastLED.h"
|
||||
|
||||
// H
|
||||
#include <Arduino.h>
|
||||
#include "shared/Keyboard.h"
|
||||
#include "shared/color.h"
|
||||
#include "shared/led_layout_array.h"
|
||||
#include "shared/Keyboard.h"
|
||||
#include "shared/Painter.h"
|
||||
#include "shared/settings.h"
|
||||
#include "arduino/LedRopeTCL.h"
|
||||
#include "arduino/ui_state.h"
|
||||
#include "shared/dprint.h"
|
||||
#include "fl/dbg.h"
|
||||
#include "fl/ui.h"
|
||||
#include "fl/unused.h"
|
||||
|
||||
// Spoof the midi library so it thinks it's running on an arduino.
|
||||
//#ifndef ARDUINO
|
||||
//#define ARDUINO 1
|
||||
//#endif
|
||||
|
||||
#ifdef MIDI_AUTO_INSTANCIATE
|
||||
#undef MIDI_AUTO_INSTANCIATE
|
||||
#define MIDI_AUTO_INSTANCIATE 0
|
||||
#endif
|
||||
|
||||
#include "arduino/MIDI.h"
|
||||
|
||||
|
||||
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MY_MIDI);
|
||||
|
||||
|
||||
FASTLED_TITLE("Luminescent Grand");
|
||||
FASTLED_DESCRIPTION("A midi keyboard visualizer.");
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
// Light rope and keyboard.
|
||||
LedRopeTCL led_rope(kNumKeys);
|
||||
KeyboardState keyboard;
|
||||
|
||||
|
||||
////////////////////////////////////
|
||||
// Called when the note is pressed.
|
||||
// Input:
|
||||
// channel - Ignored.
|
||||
// midi_note - Value between 21-108 which maps to the keyboard keys.
|
||||
// velocity - Value between 0-127
|
||||
void HandleNoteOn(byte channel, byte midi_note, byte velocity) {
|
||||
FL_UNUSED(channel);
|
||||
FASTLED_DBG("HandleNoteOn: midi_note = " << int(midi_note) << ", velocity = " << int(velocity));
|
||||
keyboard.HandleNoteOn(midi_note, velocity, color_selector.curr_val(), millis());
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
// Called when the note is released.
|
||||
// Input:
|
||||
// channel - Ignored.
|
||||
// midi_note - Value between 21-108 which maps to the keyboard keys.
|
||||
// velocity - Value between 0-127
|
||||
void HandleNoteOff(byte channel, byte midi_note, byte velocity) {
|
||||
FL_UNUSED(channel);
|
||||
FASTLED_DBG("HandleNoteOn: midi_note = " << int(midi_note) << ", velocity = " << int(velocity));
|
||||
keyboard.HandleNoteOff(midi_note, velocity, millis());
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
// This is uninmplemented because the test keyboard didn't
|
||||
// have this functionality. Right now the only thing it does is
|
||||
// print out that the key was pressed.
|
||||
void HandleAfterTouchPoly(byte channel, byte note, byte pressure) {
|
||||
FL_UNUSED(channel);
|
||||
keyboard.HandleAfterTouchPoly(note, pressure);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
// Detects whether the foot pedal has been touched.
|
||||
void HandleControlChange(byte channel, byte d1, byte d2) {
|
||||
FL_UNUSED(channel);
|
||||
keyboard.HandleControlChange(d1, d2);
|
||||
}
|
||||
|
||||
void HandleAfterTouchChannel(byte channel, byte pressure) {
|
||||
FL_UNUSED(channel);
|
||||
FL_UNUSED(pressure);
|
||||
#if 0 // Disabled for now.
|
||||
if (0 == pressure) {
|
||||
for (int i = 0; i < kNumKeys; ++i) {
|
||||
Key& key = keyboard.keys_[i];
|
||||
key.SetOff();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
// Called once when the app starts.
|
||||
void setup() {
|
||||
FASTLED_DBG("setup");
|
||||
// Serial port for logging.
|
||||
Serial.begin(57600);
|
||||
//start serial with midi baudrate 31250
|
||||
// Initiate MIDI communications, listen to all channels
|
||||
MY_MIDI.begin(MIDI_CHANNEL_OMNI);
|
||||
|
||||
// Connect the HandleNoteOn function to the library, so it is called upon reception of a NoteOn.
|
||||
MY_MIDI.setHandleNoteOn(HandleNoteOn);
|
||||
MY_MIDI.setHandleNoteOff(HandleNoteOff);
|
||||
MY_MIDI.setHandleAfterTouchPoly(HandleAfterTouchPoly);
|
||||
MY_MIDI.setHandleAfterTouchChannel(HandleAfterTouchChannel);
|
||||
MY_MIDI.setHandleControlChange(HandleControlChange);
|
||||
|
||||
ui_init();
|
||||
}
|
||||
|
||||
void DbgDoSimulatedKeyboardPress() {
|
||||
#ifdef DEBUG_FORCED_KEYBOARD
|
||||
static uint32_t start_time = 0;
|
||||
static bool toggle = 0;
|
||||
|
||||
const uint32_t time_on = 25;
|
||||
const uint32_t time_off = 500;
|
||||
|
||||
// Just force it on whenever this function is called.
|
||||
is_debugging = true;
|
||||
|
||||
uint32_t now = millis();
|
||||
uint32_t delta_time = now - start_time;
|
||||
|
||||
|
||||
uint32_t threshold_time = toggle ? time_off : time_on;
|
||||
if (delta_time < threshold_time) {
|
||||
return;
|
||||
}
|
||||
|
||||
int random_key = random(0, 88);
|
||||
|
||||
start_time = now;
|
||||
if (toggle) {
|
||||
HandleNoteOn(0, random_key, 64);
|
||||
} else {
|
||||
HandleNoteOff(0, random_key, 82);
|
||||
}
|
||||
toggle = !toggle;
|
||||
#endif
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////;p
|
||||
// Repeatedly called by the app.
|
||||
void loop() {
|
||||
//FASTLED_DBG("loop");
|
||||
|
||||
|
||||
// Calculate dt.
|
||||
static uint32_t s_prev_time = 0;
|
||||
uint32_t prev_time = 0;
|
||||
FASTLED_UNUSED(prev_time); // actually used in perf tests.
|
||||
uint32_t now_ms = millis();
|
||||
uint32_t delta_ms = now_ms - s_prev_time;
|
||||
s_prev_time = now_ms;
|
||||
|
||||
if (!is_debugging) {
|
||||
if (Serial.available() > 0) {
|
||||
int v = Serial.read();
|
||||
if (v == 'd') {
|
||||
is_debugging = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DbgDoSimulatedKeyboardPress();
|
||||
|
||||
const unsigned long start_time = millis();
|
||||
// Each frame we call the midi processor 100 times to make sure that all notes
|
||||
// are processed.
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
MY_MIDI.read();
|
||||
}
|
||||
|
||||
const unsigned long midi_time = millis() - start_time;
|
||||
|
||||
// Updates keyboard: releases sustained keys that.
|
||||
|
||||
const uint32_t keyboard_time_start = millis();
|
||||
|
||||
// This is kind of a hack... but give the keyboard a future time
|
||||
// so that all keys just pressed get a value > 0 for their time
|
||||
// durations.
|
||||
keyboard.Update(now_ms + delta_ms, delta_ms);
|
||||
const uint32_t keyboard_delta_time = millis() - keyboard_time_start;
|
||||
|
||||
ui_state ui_st = ui_update(now_ms, delta_ms);
|
||||
|
||||
//dprint("vis selector = ");
|
||||
//dprintln(vis_state);
|
||||
|
||||
|
||||
// These int values are for desting the performance of the
|
||||
// app. If the app ever runs slow then set kShowFps to 1
|
||||
// in the settings.h file.
|
||||
const unsigned long start_painting = millis();
|
||||
FASTLED_UNUSED(start_painting);
|
||||
|
||||
|
||||
// Paints the keyboard using the led_rope.
|
||||
Painter::VisState which_vis = Painter::VisState(ui_st.which_visualizer);
|
||||
Painter::Paint(now_ms, delta_ms, which_vis, &keyboard, &led_rope);
|
||||
|
||||
const unsigned long paint_time = millis() - start_time;
|
||||
const unsigned long total_time = midi_time + paint_time + keyboard_delta_time;
|
||||
|
||||
if (kShowFps) {
|
||||
float fps = 1.0f/(float(total_time) / 1000.f);
|
||||
Serial.print("fps - "); Serial.println(fps);
|
||||
Serial.print("midi time - "); Serial.println(midi_time);
|
||||
Serial.print("keyboard update time - "); Serial.println(keyboard_delta_time);
|
||||
Serial.print("draw & paint time - "); Serial.println(paint_time);
|
||||
}
|
||||
|
||||
EVERY_N_SECONDS(1) {
|
||||
FASTLED_DBG("is_debugging = " << is_debugging);
|
||||
}
|
||||
|
||||
|
||||
FastLED.show();
|
||||
}
|
||||
|
||||
|
||||
#endif // __AVR__
|
||||
@@ -0,0 +1,179 @@
|
||||
// Copyleft (c) 2012, Zach Vorhies
|
||||
// Public domain, no rights reserved.
|
||||
// This object holds a frame buffer and effects can be applied. This is a higher level
|
||||
// object than the TCL class which this object uses for drawing.
|
||||
|
||||
//#include "./tcl.h"
|
||||
#include <Arduino.h>
|
||||
#include "../shared/color.h"
|
||||
#include "../shared/framebuffer.h"
|
||||
#include "../shared/settings.h"
|
||||
#include "./LedRopeTCL.h"
|
||||
#include "../shared/led_layout_array.h"
|
||||
|
||||
#include "FastLED.h"
|
||||
#include "fl/dbg.h"
|
||||
#include "fl/ui.h"
|
||||
|
||||
using namespace fl;
|
||||
|
||||
|
||||
#define CHIPSET WS2812
|
||||
#define PIN_DATA 1
|
||||
#define PIN_CLOCK 2
|
||||
|
||||
namespace {
|
||||
|
||||
UIButton buttonAllWhite("All white");
|
||||
|
||||
ScreenMap init_screenmap() {
|
||||
LedColumns cols = LedLayoutArray();
|
||||
const int length = cols.length;
|
||||
int sum = 0;
|
||||
for (int i = 0; i < length; ++i) {
|
||||
sum += cols.array[i];
|
||||
}
|
||||
ScreenMap screen_map(sum, 0.8f);
|
||||
int curr_idx = 0;
|
||||
for (int i = 0; i < length; ++i) {
|
||||
int n = cols.array[i];
|
||||
int stagger = i % 2 ? 4 : 0;
|
||||
for (int j = 0; j < n; ++j) {
|
||||
fl::vec2f xy(i*4, j*8 + stagger);
|
||||
screen_map.set(curr_idx++, xy);
|
||||
}
|
||||
}
|
||||
|
||||
return screen_map;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void LedRopeTCL::PreDrawSetup() {
|
||||
if (!lazy_initialized_) {
|
||||
// This used to do something, now it does nothing.
|
||||
lazy_initialized_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void LedRopeTCL::RawBeginDraw() {
|
||||
PreDrawSetup();
|
||||
led_buffer_.clear();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void LedRopeTCL::RawDrawPixel(const Color3i& c) {
|
||||
RawDrawPixel(c.r_, c.g_, c.b_);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void LedRopeTCL::RawDrawPixel(byte r, byte g, byte b) {
|
||||
if (led_buffer_.size() >= mScreenMap.getLength()) {
|
||||
return;
|
||||
}
|
||||
if (buttonAllWhite.isPressed()) {
|
||||
r = 0xff;
|
||||
g = 0xff;
|
||||
b = 0xff;
|
||||
}
|
||||
CRGB c(r, g, b);
|
||||
led_buffer_.push_back(CRGB(r, g, b));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void LedRopeTCL::RawDrawPixels(const Color3i& c, int n) {
|
||||
for (int i = 0; i < n; ++i) {
|
||||
RawDrawPixel(c);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void LedRopeTCL::set_draw_offset(int val) {
|
||||
draw_offset_ = constrain(val, 0, frame_buffer_.length());
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void LedRopeTCL::RawCommitDraw() {
|
||||
FASTLED_WARN("\n\n############## COMMIT DRAW ################\n\n");
|
||||
if (!controller_added_) {
|
||||
controller_added_ = true;
|
||||
CRGB* leds = led_buffer_.data();
|
||||
size_t n_leds = led_buffer_.size();
|
||||
FastLED.addLeds<APA102, PIN_DATA, PIN_CLOCK>(leds, n_leds).setScreenMap(mScreenMap);
|
||||
}
|
||||
FASTLED_WARN("FastLED.show");
|
||||
FastLED.show();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
LedRopeTCL::LedRopeTCL(int n_pixels)
|
||||
: draw_offset_(0), lazy_initialized_(false), frame_buffer_(n_pixels) {
|
||||
mScreenMap = init_screenmap();
|
||||
led_buffer_.reserve(mScreenMap.getLength());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
LedRopeTCL::~LedRopeTCL() {
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void LedRopeTCL::Draw() {
|
||||
RawBeginDraw();
|
||||
|
||||
const Color3i* begin = GetIterator(0);
|
||||
const Color3i* middle = GetIterator(draw_offset_);
|
||||
const Color3i* end = GetIterator(length() - 1);
|
||||
|
||||
for (const Color3i* it = middle; it != end; ++it) {
|
||||
RawDrawPixel(*it);
|
||||
}
|
||||
for (const Color3i* it = begin; it != middle; ++it) {
|
||||
RawDrawPixel(*it);
|
||||
}
|
||||
RawCommitDraw();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void LedRopeTCL::DrawSequentialRepeat(int repeat) {
|
||||
RawBeginDraw();
|
||||
|
||||
const Color3i* begin = GetIterator(0);
|
||||
const Color3i* middle = GetIterator(draw_offset_);
|
||||
const Color3i* end = GetIterator(length());
|
||||
for (const Color3i* it = middle; it != end; ++it) {
|
||||
for (int i = 0; i < repeat; ++i) {
|
||||
RawDrawPixel(it->r_, it->g_, it->b_);
|
||||
}
|
||||
}
|
||||
for (const Color3i* it = begin; it != middle; ++it) {
|
||||
for (int i = 0; i < repeat; ++i) {
|
||||
RawDrawPixel(it->r_, it->g_, it->b_);
|
||||
}
|
||||
}
|
||||
RawCommitDraw();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void LedRopeTCL::DrawRepeat(const int* value_array, int array_length) {
|
||||
RawBeginDraw();
|
||||
|
||||
// Make sure that the number of colors to repeat does not exceed the length
|
||||
// of the rope.
|
||||
const int len = MIN(array_length, frame_buffer_.length());
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
const Color3i* cur_color = GetIterator(i); // Current color.
|
||||
const int repeat_count = value_array[i];
|
||||
// Repeatedly send the same color down the led rope.
|
||||
for (int k = 0; k < repeat_count; ++k) {
|
||||
RawDrawPixel(cur_color->r_, cur_color->g_, cur_color->b_);
|
||||
}
|
||||
}
|
||||
// Finish the drawing.
|
||||
RawCommitDraw();
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user