first commit
This commit is contained in:
commit
5893b00dd2
1669 changed files with 1982740 additions and 0 deletions
69
libraries/FastLED/examples/AnalogOutput/AnalogOutput.ino
Normal file
69
libraries/FastLED/examples/AnalogOutput/AnalogOutput.ino
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/// @file AnalogOutput.ino
|
||||
/// @brief Demonstrates how to use FastLED color functions even without a "pixel-addressible" smart LED strip.
|
||||
/// @example AnalogOutput.ino
|
||||
|
||||
#include <FastLED.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();
|
||||
}
|
||||
|
||||
38
libraries/FastLED/examples/Apa102/Apa102.ino
Normal file
38
libraries/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
libraries/FastLED/examples/Apa102HD/Apa102HD.ino
Normal file
88
libraries/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.
|
||||
}
|
||||
234
libraries/FastLED/examples/Audio/Audio.ino
Normal file
234
libraries/FastLED/examples/Audio/Audio.ino
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
|
||||
|
||||
/*
|
||||
This demo is best viewed using the FastLED compiler.
|
||||
Install: pip install fastled
|
||||
Run: fastled <this sketch directory>
|
||||
This will compile and preview the sketch in the browser, and enable
|
||||
all the UI elements you see below.
|
||||
*/
|
||||
|
||||
#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 "fx.h"
|
||||
#include "fx/time.h"
|
||||
|
||||
// Sketch.
|
||||
#include "fl/function.h"
|
||||
|
||||
using namespace fl;
|
||||
|
||||
#define HEIGHT 128
|
||||
#define WIDTH 128
|
||||
#define NUM_LEDS ((WIDTH) * (HEIGHT))
|
||||
#define IS_SERPINTINE false
|
||||
#define TIME_ANIMATION 1000 // ms
|
||||
|
||||
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);
|
||||
|
||||
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, 2>(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)];
|
||||
memset(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");
|
||||
}
|
||||
// fl::clear(framebuffer);
|
||||
// fl::clear(framebuffer);
|
||||
|
||||
static uint32_t frame = 0;
|
||||
|
||||
// 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_WARN("getDBFS: " << dbfs);
|
||||
int32_t max = 0;
|
||||
for (int 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;
|
||||
for (int 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();
|
||||
}
|
||||
67
libraries/FastLED/examples/Audio/fx.h
Normal file
67
libraries/FastLED/examples/Audio/fx.h
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cmath>
|
||||
#include <cassert>
|
||||
|
||||
#include "fl/time_alpha.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 = std::abs(samples[i]) * (1.0f/32768.0f);
|
||||
peak = std::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 - std::exp(-attackRate_ * dt);
|
||||
currentLevel_ += (peak - currentLevel_) * riseFactor;
|
||||
} else {
|
||||
float decayFactor = std::exp(-decayRate_ * dt);
|
||||
currentLevel_ *= decayFactor;
|
||||
}
|
||||
|
||||
// 4) output inertia: smooth smoothedOutput_ → currentLevel_
|
||||
float outFactor = 1.0f - std::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
|
||||
};
|
||||
|
|
@ -0,0 +1,217 @@
|
|||
|
||||
|
||||
/*
|
||||
This demo is best viewed using the FastLED compiler.
|
||||
Install: pip install fastled
|
||||
Run: fastled <this sketch directory>
|
||||
This will compile and preview the sketch in the browser, and enable
|
||||
all the UI elements you see below.
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <FastLED.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"
|
||||
#include "fl/bilinear_compression.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([](float value) { time_warp.setSpeed(speed.value()); });
|
||||
maxAnimation.onChanged(
|
||||
[](float value) { shapeProgress.set_max_clamp(maxAnimation.value()); });
|
||||
|
||||
trigger.onChanged([]() {
|
||||
// shapeProgress.trigger(millis());
|
||||
FASTLED_WARN("Trigger pressed");
|
||||
});
|
||||
useWaveFx.onChanged([](bool on) {
|
||||
if (on) {
|
||||
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;
|
||||
}
|
||||
|
||||
const bool is_active =
|
||||
true || curr_alpha < maxAnimation.value() && curr_alpha > 0.0f;
|
||||
|
||||
static uint32_t frame = 0;
|
||||
frame++;
|
||||
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);
|
||||
if (!is_active) {
|
||||
alpha = 0;
|
||||
}
|
||||
Tile2x2_u8 subpixel = shape->at_subpixel(a);
|
||||
subpixel.scale(alpha);
|
||||
// subpixels.push_back(subpixel);
|
||||
raster.rasterize(subpixel);
|
||||
}
|
||||
|
||||
s_prev_alpha = curr_alpha;
|
||||
|
||||
|
||||
if (useWaveFx && is_active) {
|
||||
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();
|
||||
}
|
||||
65
libraries/FastLED/examples/BilinearCompression/src/wave.cpp
Normal file
65
libraries/FastLED/examples/BilinearCompression/src/wave.cpp
Normal file
|
|
@ -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 = WaveCrgbGradientMapPtr::New(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 = WaveCrgbGradientMapPtr::New(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 =
|
||||
NewPtr<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 = NewPtr<WaveFx>(xy_rect, args_lower);
|
||||
WaveFxPtr wave_fx_high = NewPtr<WaveFx>(xy_rect, args_upper);
|
||||
Blend2dPtr blend_stack = NewPtr<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
libraries/FastLED/examples/BilinearCompression/src/wave.h
Normal file
34
libraries/FastLED/examples/BilinearCompression/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<int> &pt, uint32_t index, uint8_t value) {
|
||||
float valuef = value / 255.0f;
|
||||
int xx = pt.x;
|
||||
int 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 {
|
||||
Ptr<CatmullRomParams> make_path(int width, int height) {
|
||||
// make a triangle.
|
||||
Ptr<CatmullRomParams> params = NewPtr<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;
|
||||
}
|
||||
10
libraries/FastLED/examples/BilinearCompression/src/xypaths.h
Normal file
10
libraries/FastLED/examples/BilinearCompression/src/xypaths.h
Normal file
|
|
@ -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);
|
||||
73
libraries/FastLED/examples/Blink/Blink.ino
Normal file
73
libraries/FastLED/examples/Blink/Blink.ino
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
/// @file Blink.ino
|
||||
/// @brief Blink the first LED of an LED strip
|
||||
/// @example Blink.ino
|
||||
|
||||
#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() {
|
||||
// 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
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
72
libraries/FastLED/examples/BlinkParallel/BlinkParallel.ino
Normal file
72
libraries/FastLED/examples/BlinkParallel/BlinkParallel.ino
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
|
||||
/// @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"
|
||||
|
||||
// How many leds in your strip?
|
||||
#define NUM_LEDS 256
|
||||
|
||||
// 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
|
||||
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() {
|
||||
// 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);
|
||||
|
||||
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
libraries/FastLED/examples/Blur/Blur.ino
Normal file
32
libraries/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);
|
||||
}
|
||||
45
libraries/FastLED/examples/Blur2d/Blur2d.ino
Normal file
45
libraries/FastLED/examples/Blur2d/Blur2d.ino
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
// UIDescription: This example shows how to blur a strip of LEDs in 2d.
|
||||
|
||||
#include "fl/ui.h"
|
||||
#include "fl/xymap.h"
|
||||
#include <FastLED.h>
|
||||
|
||||
using namespace fl;
|
||||
|
||||
#define WIDTH 22
|
||||
#define HEIGHT 22
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
569
libraries/FastLED/examples/Chromancer/Chromancer.ino
Normal file
569
libraries/FastLED/examples/Chromancer/Chromancer.ino
Normal file
|
|
@ -0,0 +1,569 @@
|
|||
/*
|
||||
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
|
||||
*/
|
||||
|
||||
#if defined(__AVR__) || defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_ARCH_RP2350) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_TEENSYLC)
|
||||
// Avr is not powerful enough.
|
||||
// Other platforms have weird issues. Will revisit this later.
|
||||
void setup() {}
|
||||
void loop() {}
|
||||
#else
|
||||
|
||||
#include "mapping.h"
|
||||
#include "net.h"
|
||||
#include "ripple.h"
|
||||
#include <FastLED.h>
|
||||
#include "detail.h"
|
||||
#include "fl/screenmap.h"
|
||||
#include "fl/math_macros.h"
|
||||
#include "fl/json.h"
|
||||
#include "fl/ui.h"
|
||||
#include "fl/map.h"
|
||||
|
||||
#include "screenmap.json.h"
|
||||
#include "fl/str.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;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
Serial.println("*** LET'S GOOOOO ***");
|
||||
|
||||
Serial.println("JSON SCREENMAP");
|
||||
Serial.println(JSON_SCREEN_MAP);
|
||||
|
||||
FixedMap<Str, ScreenMap, 16> 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;
|
||||
ok = segmentMaps.get("red_segment", &red) && ok;
|
||||
ok = segmentMaps.get("back_segment", &black) && ok;
|
||||
ok = segmentMaps.get("green_segment", &green) && ok;
|
||||
ok = segmentMaps.get("blue_segment", &blue) && ok;
|
||||
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();
|
||||
net_init();
|
||||
}
|
||||
|
||||
|
||||
void loop() {
|
||||
unsigned long benchmark = millis();
|
||||
net_loop();
|
||||
|
||||
|
||||
|
||||
// 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
libraries/FastLED/examples/Chromancer/detail.h
Normal file
80
libraries/FastLED/examples/Chromancer/detail.h
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#pragma once
|
||||
|
||||
#include <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
libraries/FastLED/examples/Chromancer/gen.py
Normal file
270
libraries/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
libraries/FastLED/examples/Chromancer/mapping.h
Normal file
158
libraries/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
|
||||
168
libraries/FastLED/examples/Chromancer/net.h
Normal file
168
libraries/FastLED/examples/Chromancer/net.h
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <FastLED.h>
|
||||
|
||||
#define OTA_SUPPORTED 0
|
||||
#define WIFI_SUPPORTED 0
|
||||
|
||||
#if OTA_SUPPORTED && !WIFI_SUPPORTED
|
||||
#error "You can't have OTA without WiFi, dummy"
|
||||
#endif
|
||||
|
||||
#if WIFI_SUPPORTED
|
||||
#include <ArduinoOSC.h>
|
||||
#include <ESPmDNS.h>
|
||||
#include <WiFi.h>
|
||||
#include <WiFiUdp.h>
|
||||
const char *ssid = "YourMom";
|
||||
const char *password = "is a nice lady";
|
||||
// WiFi stuff - CHANGE FOR YOUR OWN NETWORK!
|
||||
const IPAddress ip(4, 20, 6, 9); // IP address that THIS DEVICE should request
|
||||
const IPAddress gateway(192, 168, 1, 1); // Your router
|
||||
const IPAddress
|
||||
subnet(255, 255, 254,
|
||||
0); // Your subnet mask (find it from your router's admin panel)
|
||||
const int recv_port = 42069; // Port that OSC data should be sent to (pick one,
|
||||
// put same one in EmotiBit's OSC Config XML file)
|
||||
#endif
|
||||
|
||||
#if OTA_SUPPORTED
|
||||
#include <ArduinoOTA.h>
|
||||
#endif
|
||||
|
||||
void net_init();
|
||||
void net_loop();
|
||||
|
||||
void net_init() {
|
||||
#if WIFI_SUPPORTED
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
WiFi.config(ip, gateway, subnet);
|
||||
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||
Serial.println("Connection Failed! Rebooting...");
|
||||
delay(5000);
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
Serial.print("WiFi connected, IP = ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
// Subscribe to OSC transmissions for important data
|
||||
OscWiFi.subscribe(
|
||||
recv_port, "/EmotiBit/0/EDA",
|
||||
[](const OscMessage &m) { // This weird syntax is a lambda expression
|
||||
// (anonymous nameless function)
|
||||
lastKnownTemperature = m.arg<float>(0);
|
||||
});
|
||||
|
||||
OscWiFi.subscribe(recv_port, "/EmotiBit/0/GYRO:X", [](const OscMessage &m) {
|
||||
gyroX = m.arg<float>(0) * gyroAlpha + gyroX * (1 - gyroAlpha);
|
||||
});
|
||||
|
||||
OscWiFi.subscribe(recv_port, "/EmotiBit/0/GYRO:Y", [](const OscMessage &m) {
|
||||
gyroY = m.arg<float>(0) * gyroAlpha + gyroY * (1 - gyroAlpha);
|
||||
});
|
||||
|
||||
OscWiFi.subscribe(recv_port, "/EmotiBit/0/GYRO:Z", [](const OscMessage &m) {
|
||||
gyroZ = m.arg<float>(0) * gyroAlpha + gyroZ * (1 - gyroAlpha);
|
||||
});
|
||||
|
||||
// Heartbeat detection and visualization happens here
|
||||
OscWiFi.subscribe(recv_port, "/EmotiBit/0/PPG:IR", [](const OscMessage &m) {
|
||||
float reading = m.arg<float>(0);
|
||||
Serial.println(reading);
|
||||
|
||||
int hue = 0;
|
||||
|
||||
// Ignore heartbeat when finger is wiggling around - it's not accurate
|
||||
float gyroTotal = abs(gyroX) + abs(gyroY) + abs(gyroZ);
|
||||
|
||||
if (gyroTotal < gyroThreshold && lastIrReading >= reading) {
|
||||
// Our hand is sitting still and the reading dropped - let's pulse!
|
||||
Serial.print("> ");
|
||||
Serial.println(highestIrReading - reading);
|
||||
if (highestIrReading - reading >= heartbeatDelta) {
|
||||
if (millis() - lastHeartbeat >= heartbeatLockout) {
|
||||
hue = fmap(lastKnownTemperature, lowTemperature,
|
||||
highTemperature, 0xFFFF, 0);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (nodeConnections[15][i] > 0) {
|
||||
bool firedRipple = false;
|
||||
// Find a dead ripple to reuse it
|
||||
for (int j = 0; j < 30; j++) {
|
||||
if (!firedRipple && ripples[j].state == dead) {
|
||||
ripples[j].start(
|
||||
15, i, strip0.ColorHSV(hue, 255, 255),
|
||||
float(random(100)) / 100.0 * .2 + .8,
|
||||
500, 2);
|
||||
|
||||
firedRipple = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastHeartbeat = millis();
|
||||
}
|
||||
} else {
|
||||
highestIrReading = 0;
|
||||
}
|
||||
|
||||
lastIrReading = reading;
|
||||
if (reading > highestIrReading)
|
||||
highestIrReading = reading;
|
||||
});
|
||||
#endif // WIFI_SUPPORTED
|
||||
|
||||
#if OTA_SUPPORTED
|
||||
// Wireless OTA updating? On an ARDUINO?! It's more likely than you think!
|
||||
ArduinoOTA
|
||||
.onStart([]() {
|
||||
String type;
|
||||
if (ArduinoOTA.getCommand() == U_FLASH)
|
||||
type = "sketch";
|
||||
else // U_SPIFFS
|
||||
type = "filesystem";
|
||||
|
||||
// NOTE: if updating SPIFFS this would be the place to unmount
|
||||
// SPIFFS using SPIFFS.end()
|
||||
Serial.println("Start updating " + type);
|
||||
})
|
||||
.onEnd([]() { Serial.println("\nEnd"); })
|
||||
.onProgress([](unsigned int progress, unsigned int total) {
|
||||
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
|
||||
})
|
||||
.onError([](ota_error_t error) {
|
||||
Serial.printf("Error[%u]: ", error);
|
||||
if (error == OTA_AUTH_ERROR)
|
||||
Serial.println("Auth Failed");
|
||||
else if (error == OTA_BEGIN_ERROR)
|
||||
Serial.println("Begin Failed");
|
||||
else if (error == OTA_CONNECT_ERROR)
|
||||
Serial.println("Connect Failed");
|
||||
else if (error == OTA_RECEIVE_ERROR)
|
||||
Serial.println("Receive Failed");
|
||||
else if (error == OTA_END_ERROR)
|
||||
Serial.println("End Failed");
|
||||
});
|
||||
|
||||
ArduinoOTA.begin();
|
||||
|
||||
Serial.println("Ready for WiFi OTA updates");
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
#endif
|
||||
}
|
||||
|
||||
void net_loop() {
|
||||
#if WIFI_SUPPORTED
|
||||
OscWiFi.parse();
|
||||
#endif
|
||||
|
||||
#if OTA_SUPPORTED
|
||||
ArduinoOTA.handle();
|
||||
#endif
|
||||
|
||||
}
|
||||
1
libraries/FastLED/examples/Chromancer/output.json
Normal file
1
libraries/FastLED/examples/Chromancer/output.json
Normal file
File diff suppressed because one or more lines are too long
424
libraries/FastLED/examples/Chromancer/ripple.h
Normal file
424
libraries/FastLED/examples/Chromancer/ripple.h
Normal file
|
|
@ -0,0 +1,424 @@
|
|||
/*
|
||||
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];
|
||||
|
||||
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
|
||||
4
libraries/FastLED/examples/Chromancer/screenmap.json.h
Normal file
4
libraries/FastLED/examples/Chromancer/screenmap.json.h
Normal file
File diff suppressed because one or more lines are too long
203
libraries/FastLED/examples/ColorPalette/ColorPalette.ino
Normal file
203
libraries/FastLED/examples/ColorPalette/ColorPalette.ino
Normal file
|
|
@ -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 );
|
||||
}
|
||||
|
||||
60
libraries/FastLED/examples/Cylon/Cylon.ino
Normal file
60
libraries/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);
|
||||
}
|
||||
}
|
||||
125
libraries/FastLED/examples/DemoReel100/DemoReel100.ino
Normal file
125
libraries/FastLED/examples/DemoReel100/DemoReel100.ino
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
/// @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);
|
||||
}
|
||||
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
208
libraries/FastLED/examples/Esp32S3I2SDemo/Esp32S3I2SDemo.ino
Normal file
208
libraries/FastLED/examples/Esp32S3I2SDemo/Esp32S3I2SDemo.ino
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
|
||||
|
||||
/// The Yves ESP32_S3 I2S driver is a driver that uses the I2S peripheral on the ESP32-S3 to drive leds.
|
||||
/// Originally from: https://github.com/hpwit/I2SClockLessLedDriveresp32s3
|
||||
///
|
||||
/// This is an advanced driver. It has certain ramifications.
|
||||
/// - You probably aren't going to be able to use this in ArduinoIDE, because ArduinoIDE does not allow you to put in the necessary build flags.
|
||||
/// You will need to use PlatformIO to build this.
|
||||
/// - These flags enable PSRAM.
|
||||
/// - Once flashed, the ESP32-S3 might NOT want to be reprogrammed again. To get around
|
||||
/// this hold the reset button and release when the flash tool is looking for an
|
||||
/// an upload port.
|
||||
/// - Put a delay in the setup function. This is to make it easier to flash the device.
|
||||
/// - Serial output will mess up the DMA controller. I'm not sure why this is happening
|
||||
/// but just be aware of it. If your device suddenly stops works, remove the printfs and see if that fixes the problem.
|
||||
/// - You MUST use all the available PINS specified in this demo. Anything less than that will cause FastLED to crash.
|
||||
/// - Certain leds will turn white in debug mode. Probably has something to do with timing.
|
||||
///
|
||||
/// Is RGBW supported? Yes.
|
||||
///
|
||||
/// Is Overclocking supported? No.
|
||||
///
|
||||
/// What about the new WS2812-5VB leds? Kinda. We put in a hack to add the extra wait time of 300 uS.
|
||||
///
|
||||
/// What are the advantages of using the FastLED bindings over the raw driver?
|
||||
/// - FastLED api is more user friendly since you don't have to combine all your leds into one rectangular block.
|
||||
/// - FastLED api allows you to have different sized strips which will be upscaled to the largest strip internally.
|
||||
///
|
||||
/// What are the advantages of using the raw driver over the FastLED bindings?
|
||||
/// - The raw driver uses less memory because it doesn't have a frame buffer copy.
|
||||
///
|
||||
// PLATFORMIO BUILD FLAGS:
|
||||
// Define your platformio.ini like so:
|
||||
//
|
||||
// [env:esp32s3]
|
||||
// platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.04/platform-espressif32.zip
|
||||
// framework = arduino
|
||||
// board = seeed_xiao_esp32s3
|
||||
// build_flags =
|
||||
// ${env:generic-esp.build_flags}
|
||||
// -DBOARD_HAS_PSRAM
|
||||
// -mfix-esp32-psram-cache-issue
|
||||
// -mfix-esp32-psram-cache-strategy=memw
|
||||
// board_build.partitions = huge_app.csv
|
||||
//
|
||||
// Then in your setup function you are going to want to call psramInit();
|
||||
//
|
||||
// Want to get a contributor badge for FastLED? This driver has only been lightly tested.
|
||||
// There are certain open questions:
|
||||
// - Can the pins order for the strips be changed? (the pins can be defined arbitrarily,Tested on esp32s3, esp32duino version 3.2.0)
|
||||
// - Are there some combination of pins that can be ommitted?
|
||||
// - What other caveats are there?
|
||||
//
|
||||
// If you know the answer to these questions then please submit a PR to the FastLED repo and
|
||||
// we will update the information for the community.
|
||||
|
||||
#include <esp_psram.h>
|
||||
|
||||
#define FASTLED_USES_ESP32S3_I2S
|
||||
|
||||
#include "FastLED.h"
|
||||
#include "fl/assert.h"
|
||||
|
||||
|
||||
#define NUMSTRIPS 16
|
||||
#define NUM_LEDS_PER_STRIP 256
|
||||
#define NUM_LEDS (NUM_LEDS_PER_STRIP * NUMSTRIPS)
|
||||
|
||||
#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
|
||||
|
||||
|
||||
const bool gUseFastLEDApi = true; // Set this to false to use the raw driver.
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
fl::InternalI2SDriver *driver = nullptr;
|
||||
CRGB leds[NUM_LEDS];
|
||||
|
||||
void setup_i2s_using_fastled_api() {
|
||||
// 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
|
||||
);
|
||||
FastLED.setBrightness(32);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void setup() {
|
||||
psramInit(); // IMPORTANT: This is required to enable PSRAM. If you don't do this, the driver will not work.
|
||||
// put your setup code here, to run once:
|
||||
Serial.begin(115200);
|
||||
|
||||
// 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 second before startup");
|
||||
delay(6000); // The long reset time here is to make it easier to flash the device during the development process.
|
||||
if (gUseFastLEDApi) {
|
||||
setup_i2s_using_fastled_api();
|
||||
} else {
|
||||
driver = fl::InternalI2SDriver::create();
|
||||
driver->initled((uint8_t *)leds, PINS, NUMSTRIPS, NUM_LEDS_PER_STRIP); // Skips extra frame buffer copy.
|
||||
driver->setBrightness(32);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
if (gUseFastLEDApi) {
|
||||
FastLED.show();
|
||||
} else {
|
||||
FASTLED_ASSERT(driver != nullptr, "Did not expect driver to be null");
|
||||
driver->show();
|
||||
}
|
||||
}
|
||||
46
libraries/FastLED/examples/EspI2SDemo/EspI2SDemo.ino
Normal file
46
libraries/FastLED/examples/EspI2SDemo/EspI2SDemo.ino
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
// 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.
|
||||
|
||||
|
||||
#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++;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
#include "old.h"
|
||||
212
libraries/FastLED/examples/FestivalStick/curr.h
Normal file
212
libraries/FastLED/examples/FestivalStick/curr.h
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
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/corkscrew.h"
|
||||
#include "fl/screenmap.h"
|
||||
#include "fl/warn.h"
|
||||
#include "fl/sstream.h"
|
||||
#include "fl/leds.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 CORKSCREW_TOTAL_HEIGHT \
|
||||
23.25f // Total height of the corkscrew in centimeters for 144 densly
|
||||
// wrapped up over 19 turns
|
||||
#define CORKSCREW_TURNS 19 // Default to 19 turns
|
||||
// #define CM_BETWEEN_LEDS 1.0 // 1cm between LEDs
|
||||
// #define CM_LED_DIAMETER 0.5 // 0.5cm LED diameter
|
||||
|
||||
#define CORKSCREW_WIDTH 16
|
||||
#define CORKSCREW_HEIGHT 19
|
||||
|
||||
|
||||
UITitle festivalStickTitle("Festival Stick");
|
||||
UIDescription festivalStickDescription(
|
||||
"Take a wooden walking stick, wrap dense LEDs around it like a corkscrew. "
|
||||
"Super simple but very awesome looking."
|
||||
"This assumes the dense 144 LEDs / meter.");
|
||||
|
||||
UISlider ledsScale("Leds scale", 0.1f, 0.1f, 1.0f, 0.01f);
|
||||
UIButton button("Button");
|
||||
|
||||
CRGB leds[NUM_LEDS];
|
||||
|
||||
// Tested on a 288 led (2x 144 max density led strip) with 19 turns
|
||||
// with 23.25cm height, 19 turns, and ~15.5 LEDs per turn.
|
||||
Corkscrew::Input
|
||||
corkscrewInput(CORKSCREW_TOTAL_HEIGHT,
|
||||
CORKSCREW_TURNS * 2.0f * PI, // Default to 19 turns
|
||||
0, // offset to account for gaps between segments
|
||||
NUM_LEDS, // Default to dense 144 leds.
|
||||
);
|
||||
|
||||
// Corkscrew::Output corkscrewMap = fl::Corkscrew::generateMap(corkscrewInput);
|
||||
Corkscrew corkscrew(corkscrewInput);
|
||||
|
||||
// Used only for the fl::ScreenMap generation.
|
||||
struct corkscrew_args {
|
||||
int num_leds = NUM_LEDS;
|
||||
float leds_per_turn = 15.5;
|
||||
float width_cm = 1.0;
|
||||
};
|
||||
|
||||
fl::ScreenMap makeScreenMap(corkscrew_args args = corkscrew_args()) {
|
||||
// Create a ScreenMap for the corkscrew
|
||||
fl::vector<vec2f> points(args.num_leds);
|
||||
|
||||
int num_leds = args.num_leds;
|
||||
float leds_per_turn = args.leds_per_turn;
|
||||
float width_cm = args.width_cm;
|
||||
|
||||
const float circumference = leds_per_turn;
|
||||
const float radius = circumference / (2.0 * PI); // radius in mm
|
||||
const float angle_per_led = 2.0 * PI / leds_per_turn; // degrees per LED
|
||||
const float height_per_turn_cm = width_cm; // 10cm height per turn
|
||||
const float height_per_led = height_per_turn_cm / leds_per_turn *
|
||||
1.3; // this is the changing height per led.
|
||||
|
||||
for (int i = 0; i < num_leds; i++) {
|
||||
float angle = i * angle_per_led; // angle in radians
|
||||
float r = radius + 10 + i * height_per_led; // height in cm
|
||||
|
||||
// Calculate the x, y coordinates for the corkscrew
|
||||
float x = r * cos(angle); // x coordinate
|
||||
float y = r * sin(angle); // y coordinate
|
||||
|
||||
// Store the 2D coordinates in the vector
|
||||
points[i] = vec2f(x, y);
|
||||
}
|
||||
|
||||
FASTLED_WARN("Creating ScreenMap with:\n" << points);
|
||||
|
||||
// Create a ScreenMap from the points
|
||||
fl::ScreenMap screenMap(points.data(), num_leds, .5);
|
||||
return screenMap;
|
||||
}
|
||||
|
||||
// Create a corkscrew with:
|
||||
// - 30cm total length (300mm)
|
||||
// - 5cm width (50mm)
|
||||
// - 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;
|
||||
|
||||
CLEDController *addController() {
|
||||
CLEDController *controller =
|
||||
&FastLED.addLeds<APA102HD, PIN_DATA, PIN_CLOCK, BGR>(leds, NUM_LEDS);
|
||||
return controller;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
pinMode(PIN_GRND, OUTPUT);
|
||||
digitalWrite(PIN_GRND, LOW); // Set ground pin to low
|
||||
button.addRealButton(Button(PIN_BUTTON));
|
||||
corkscrew_args args = corkscrew_args();
|
||||
screenMap = makeScreenMap(args);
|
||||
// screenMap = ScreenMap::Circle(NUM_LEDS, 1.5f, 0.5f, 1.0f);
|
||||
auto controller = addController();
|
||||
// Set the screen map for the controller
|
||||
controller->setScreenMap(screenMap);
|
||||
|
||||
// Set power management. This allows this festival stick to conformatable
|
||||
// run on any USB battery that can output at least 1A at 5V.
|
||||
// Keep in mind that this sketch is designed to use APA102HD mode, which
|
||||
// will result in even lowwer run power consumption, since the power mode
|
||||
// does not take into account the APA102HD gamma correction. However it is
|
||||
// still a correct upper bound that will match the ledset exactly when the
|
||||
// display tries to go full white.
|
||||
FastLED.setMaxPowerInVoltsAndMilliamps(VOLTS, MAX_AMPS * 1000);
|
||||
button.onChanged([](UIButton &but) {
|
||||
// This function is called when the button is pressed
|
||||
// If the button is pressed, show the generative pattern
|
||||
if (but.isPressed()) {
|
||||
FASTLED_WARN("Button pressed");
|
||||
} else {
|
||||
FASTLED_WARN("NOT Button pressed");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void printOutput(const Corkscrew::Output& output) {
|
||||
fl::sstream stream;
|
||||
stream << "Corkscrew Output:\n";
|
||||
stream << "Width: " << output.width << "\n";
|
||||
stream << "Height: " << output.height << "\n";
|
||||
// stream << "Mapping: \n";
|
||||
// for (const auto &point : output.mapping) {
|
||||
// stream << point << "\n";
|
||||
// }
|
||||
FASTLED_WARN(stream.str());
|
||||
}
|
||||
|
||||
|
||||
|
||||
LedsXY<CORKSCREW_WIDTH, CORKSCREW_HEIGHT> frameBuffer;
|
||||
|
||||
void loop() {
|
||||
uint32_t now = millis();
|
||||
fl::clear(leds);
|
||||
fl::clear(frameBuffer);
|
||||
|
||||
static int w = 0;
|
||||
|
||||
EVERY_N_MILLIS(300) {
|
||||
// Update the corkscrew mapping every second
|
||||
w = (w + 1) % CORKSCREW_WIDTH;
|
||||
}
|
||||
|
||||
|
||||
// draw a blue line down the middle
|
||||
for (int i = 0; i < CORKSCREW_HEIGHT; ++i) {
|
||||
frameBuffer.at(w % CORKSCREW_WIDTH, i) = CRGB::Blue;
|
||||
frameBuffer.at((w + 1) % CORKSCREW_WIDTH, i) = CRGB::Blue;
|
||||
frameBuffer.at((w - 1 + CORKSCREW_WIDTH) % CORKSCREW_WIDTH, i) = CRGB::Blue;
|
||||
frameBuffer.at((w + 2) % CORKSCREW_WIDTH, i) = CRGB::Blue;
|
||||
frameBuffer.at((w - 2 + CORKSCREW_WIDTH) % CORKSCREW_WIDTH, i) = CRGB::Blue;
|
||||
}
|
||||
|
||||
|
||||
// printOutput(corkscrewMap);
|
||||
|
||||
for (int i = 0; i < NUM_LEDS; ++i) {
|
||||
// Get the position in the frame buffer
|
||||
vec2<int16_t> pos = corkscrew.at(i);
|
||||
// Draw the tile to the frame buffer
|
||||
CRGB c = frameBuffer.at(pos.x, pos.y);
|
||||
leds[i] = c;
|
||||
|
||||
FASTLED_WARN_IF(i < 16, "LED " << i << " at position: "
|
||||
<< pos.x << ", " << pos.y
|
||||
<< " with color: " << c);
|
||||
|
||||
}
|
||||
|
||||
FastLED.show();
|
||||
}
|
||||
210
libraries/FastLED/examples/FestivalStick/old.h
Normal file
210
libraries/FastLED/examples/FestivalStick/old.h
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
/*
|
||||
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");
|
||||
UIDescription festivalStickDescription(
|
||||
"Take a wooden walking stick, wrap dense LEDs around it like a corkscrew. Super simple but very awesome looking."
|
||||
"This assumes the dense 144 LEDs / meter.");
|
||||
|
||||
|
||||
|
||||
UISlider ledsScale("Leds scale", 0.1f, 0.1f, 1.0f, 0.01f);
|
||||
UIButton button("Button");
|
||||
|
||||
|
||||
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);
|
||||
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();
|
||||
}
|
||||
109
libraries/FastLED/examples/Fire2012/Fire2012.ino
Normal file
109
libraries/FastLED/examples/Fire2012/Fire2012.ino
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
/// @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];
|
||||
|
||||
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( random());
|
||||
|
||||
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,166 @@
|
|||
/// @file Fire2012WithPalette.ino
|
||||
/// @brief Simple one-dimensional fire animation with a programmable color palette
|
||||
/// @example Fire2012WithPalette.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];
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Add entropy to random number generator; we use a lot of it.
|
||||
random16_add_entropy( random());
|
||||
|
||||
// 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);
|
||||
}
|
||||
252
libraries/FastLED/examples/Fire2023/Fire2023.ino
Normal file
252
libraries/FastLED/examples/Fire2023/Fire2023.ino
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
/*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 18
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
207
libraries/FastLED/examples/FireCylinder/FireCylinder.ino
Normal file
207
libraries/FastLED/examples/FireCylinder/FireCylinder.ino
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
|
||||
/*
|
||||
This demo is best viewed using the FastLED compiler.
|
||||
Install: pip install fastled
|
||||
Run: fastled <this sketch directory>
|
||||
This will compile and preview the sketch in the browser, and enable
|
||||
all the UI elements you see below.
|
||||
|
||||
OVERVIEW:
|
||||
This sketch creates a fire effect on a cylindrical LED display using Perlin noise.
|
||||
Unlike a flat matrix, this cylinder connects the left and right edges (x=0 and x=width-1),
|
||||
creating a seamless wrap-around effect. The fire appears to rise from the bottom,
|
||||
with colors transitioning from black to red/yellow/white (or other palettes).
|
||||
*/
|
||||
|
||||
// Perlin noise fire procedure
|
||||
// 16x16 rgb led cylinder demo
|
||||
// Exactly the same as the FireMatrix example, but with a cylinder, meaning that the x=0
|
||||
// and x = len-1 are connected.
|
||||
// This also showcases the inoise16(x,y,z,t) function which handles 3D+time noise effects.
|
||||
// Keep in mind that a cylinder is embedded in a 3D space with time being used to add
|
||||
// additional noise to the effect.
|
||||
|
||||
// HOW THE CYLINDRICAL FIRE EFFECT WORKS:
|
||||
// 1. We use sine and cosine to map the x-coordinate to a circle in 3D space
|
||||
// 2. This creates a cylindrical mapping where the left and right edges connect seamlessly
|
||||
// 3. We use 4D Perlin noise (x,y,z,t) to generate natural-looking fire patterns
|
||||
// 4. The height coordinate controls color fade-out to create the rising fire effect
|
||||
// 5. The time dimension adds continuous variation to make the fire look dynamic
|
||||
|
||||
#include "FastLED.h" // Main FastLED library for controlling LEDs
|
||||
#include "fl/ui.h" // UI components for the FastLED web compiler (sliders, buttons, etc.)
|
||||
#include "fl/xymap.h" // Mapping between 1D LED array and 2D coordinates
|
||||
#include "fx/time.h" // Time manipulation utilities for animations
|
||||
|
||||
using namespace fl; // Use the FastLED namespace for convenience
|
||||
|
||||
// Cylinder dimensions - this defines the size of our virtual LED grid
|
||||
#define HEIGHT 100 // Number of rows in the cylinder (vertical dimension)
|
||||
#define WIDTH 100 // Number of columns in the cylinder (circumference)
|
||||
#define SERPENTINE true // Whether the LED strip zigzags back and forth (common in matrix layouts)
|
||||
#define BRIGHTNESS 255 // Maximum brightness level (0-255)
|
||||
|
||||
// UI elements that appear in the FastLED web compiler interface:
|
||||
UITitle title("FireCylinder Demo"); // Title displayed in the UI
|
||||
UIDescription description("This Fire demo wraps around the cylinder. It uses Perlin noise to create a fire effect.");
|
||||
|
||||
// TimeWarp helps control animation speed - it tracks time and allows speed adjustments
|
||||
TimeWarp timeScale(0, 1.0f); // Initialize with 0 starting time and 1.0 speed multiplier
|
||||
|
||||
// UI Controls for adjusting the fire effect:
|
||||
UISlider scaleXY("Scale", 8, 1, 100, 1); // Controls the overall size of the fire pattern
|
||||
UISlider speedY("SpeedY", 1.3, 1, 6, .1); // Controls how fast the fire moves upward
|
||||
UISlider scaleX("ScaleX", .3, 0.1, 3, .01); // Controls the horizontal scale (affects the wrap-around)
|
||||
UISlider invSpeedZ("Inverse SpeedZ", 20, 1, 100, 1); // Controls how fast the fire pattern changes over time (higher = slower)
|
||||
UISlider brightness("Brightness", 255, 0, 255, 1); // Controls overall brightness
|
||||
UINumberField palette("Palette", 0, 0, 2); // Selects which color palette to use (0=fire, 1=green, 2=blue)
|
||||
|
||||
// Array to hold all LED color values - one CRGB struct per LED
|
||||
CRGB leds[HEIGHT * WIDTH];
|
||||
|
||||
// Color palettes define the gradient of colors used for the fire effect
|
||||
// Each entry has the format: position (0-255), R, G, B
|
||||
|
||||
DEFINE_GRADIENT_PALETTE(firepal){
|
||||
// Traditional fire palette - transitions from black to red to yellow to white
|
||||
0, 0, 0, 0, // black (bottom of fire)
|
||||
32, 255, 0, 0, // red (base of flames)
|
||||
190, 255, 255, 0, // yellow (middle of flames)
|
||||
255, 255, 255, 255 // white (hottest part/tips of flames)
|
||||
};
|
||||
|
||||
DEFINE_GRADIENT_PALETTE(electricGreenFirePal){
|
||||
// Green fire palette - for a toxic/alien look
|
||||
0, 0, 0, 0, // black (bottom)
|
||||
32, 0, 70, 0, // dark green (base)
|
||||
190, 57, 255, 20, // electric neon green (middle)
|
||||
255, 255, 255, 255 // white (hottest part)
|
||||
};
|
||||
|
||||
DEFINE_GRADIENT_PALETTE(electricBlueFirePal){
|
||||
// Blue fire palette - for a cold/ice fire look
|
||||
0, 0, 0, 0, // Black (bottom)
|
||||
32, 0, 0, 70, // Dark blue (base)
|
||||
128, 20, 57, 255, // Electric blue (middle)
|
||||
255, 255, 255, 255 // White (hottest part)
|
||||
};
|
||||
|
||||
// Create a mapping between 1D array positions and 2D x,y coordinates
|
||||
XYMap xyMap(HEIGHT, WIDTH, SERPENTINE);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200); // Initialize serial communication for debugging
|
||||
|
||||
// Initialize the LED strip:
|
||||
// - NEOPIXEL is the LED type
|
||||
// - 3 is the data pin number (for real hardware)
|
||||
// - setScreenMap connects our 2D coordinate system to the 1D LED array
|
||||
FastLED.addLeds<NEOPIXEL, 3>(leds, HEIGHT * WIDTH).setScreenMap(xyMap);
|
||||
|
||||
// Apply color correction for more accurate colors on LED strips
|
||||
FastLED.setCorrection(TypicalLEDStrip);
|
||||
}
|
||||
|
||||
uint8_t getPaletteIndex(uint32_t millis32, int width, int max_width, int height, int max_height,
|
||||
uint32_t y_speed) {
|
||||
// This function calculates which color to use from our palette for each LED
|
||||
|
||||
// Get the scale factor from the UI slider
|
||||
uint16_t scale = scaleXY.as<uint16_t>();
|
||||
|
||||
// Convert width position to an angle (0-255 represents 0-360 degrees)
|
||||
// This maps our flat coordinate to a position on a cylinder
|
||||
float xf = (float)width / (float)max_width; // Normalized position (0.0 to 1.0)
|
||||
uint8_t x = (uint8_t)(xf * 255); // Convert to 0-255 range for trig functions
|
||||
|
||||
// Calculate the sine and cosine of this angle to get 3D coordinates on the cylinder
|
||||
uint32_t cosx = cos8(x); // cos8 returns a value 0-255 representing cosine
|
||||
uint32_t sinx = sin8(x); // sin8 returns a value 0-255 representing sine
|
||||
|
||||
// Apply scaling to the sine/cosine values
|
||||
// This controls how "wide" the noise pattern is around the cylinder
|
||||
float trig_scale = scale * scaleX.value();
|
||||
cosx *= trig_scale;
|
||||
sinx *= trig_scale;
|
||||
|
||||
// Calculate Y coordinate (vertical position) with speed offset for movement
|
||||
uint32_t y = height * scale + y_speed;
|
||||
|
||||
// Calculate Z coordinate (time dimension) - controls how the pattern changes over time
|
||||
uint16_t z = millis32 / invSpeedZ.as<uint16_t>();
|
||||
|
||||
// Generate 16-bit Perlin noise using our 4D coordinates (x,y,z,t)
|
||||
// The << 8 shifts values left by 8 bits (multiplies by 256) to use the full 16-bit range
|
||||
// The last parameter (0) could be replaced with another time variable for more variation
|
||||
uint16_t noise16 = inoise16(cosx << 8, sinx << 8, y << 8, 0);
|
||||
|
||||
// Convert 16-bit noise to 8-bit by taking the high byte
|
||||
uint8_t noise_val = noise16 >> 8;
|
||||
|
||||
// Calculate how much to subtract based on vertical position (height)
|
||||
// This creates the fade-out effect from bottom to top
|
||||
// The formula maps height from 0 to max_height-1 to a value from 255 to 0
|
||||
int8_t subtraction_factor = abs8(height - (max_height - 1)) * 255 /
|
||||
(max_height - 1);
|
||||
|
||||
// Subtract the factor from the noise value (with underflow protection)
|
||||
// qsub8 is a "saturating subtraction" - it won't go below 0
|
||||
return qsub8(noise_val, subtraction_factor);
|
||||
}
|
||||
CRGBPalette16 getPalette() {
|
||||
// This function returns the appropriate color palette based on the UI selection
|
||||
switch (palette) {
|
||||
case 0:
|
||||
return firepal; // Traditional orange/red fire
|
||||
case 1:
|
||||
return electricGreenFirePal; // Green "toxic" fire
|
||||
case 2:
|
||||
return electricBlueFirePal; // Blue "cold" fire
|
||||
default:
|
||||
return firepal; // Default to traditional fire if invalid value
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// The main program loop that runs continuously
|
||||
|
||||
// Set the overall brightness from the UI slider
|
||||
FastLED.setBrightness(brightness);
|
||||
|
||||
// Get the selected color palette
|
||||
CRGBPalette16 myPal = getPalette();
|
||||
|
||||
// Get the current time in milliseconds
|
||||
uint32_t now = millis();
|
||||
|
||||
// Update the animation speed from the UI slider
|
||||
timeScale.setSpeed(speedY);
|
||||
|
||||
// Calculate the current y-offset for animation (makes the fire move)
|
||||
uint32_t y_speed = timeScale.update(now);
|
||||
|
||||
// Loop through every LED in our cylindrical matrix
|
||||
for (int width = 0; width < WIDTH; width++) {
|
||||
for (int height = 0; height < HEIGHT; height++) {
|
||||
// Calculate which color to use from our palette for this LED
|
||||
// This function handles the cylindrical mapping using sine/cosine
|
||||
uint8_t palette_index =
|
||||
getPaletteIndex(now, width, WIDTH, height, HEIGHT, y_speed);
|
||||
|
||||
// Get the actual RGB color from the palette
|
||||
// BRIGHTNESS ensures we use the full brightness range
|
||||
CRGB c = ColorFromPalette(myPal, palette_index, BRIGHTNESS);
|
||||
|
||||
// Convert our 2D coordinates to the 1D array index
|
||||
// We use (WIDTH-1)-width and (HEIGHT-1)-height to flip the coordinates
|
||||
// This makes the fire appear to rise from the bottom
|
||||
int index = xyMap((WIDTH - 1) - width, (HEIGHT - 1) - height);
|
||||
|
||||
// Set the LED color in our array
|
||||
leds[index] = c;
|
||||
}
|
||||
}
|
||||
|
||||
// Send the color data to the actual LEDs
|
||||
FastLED.show();
|
||||
}
|
||||
184
libraries/FastLED/examples/FireMatrix/FireMatrix.ino
Normal file
184
libraries/FastLED/examples/FireMatrix/FireMatrix.ino
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
|
||||
|
||||
/*
|
||||
This demo is best viewed using the FastLED compiler.
|
||||
Install: pip install fastled
|
||||
Run: fastled <this sketch directory>
|
||||
This will compile and preview the sketch in the browser, and enable
|
||||
all the UI elements you see below.
|
||||
|
||||
OVERVIEW:
|
||||
This sketch creates a fire effect using Perlin noise on a matrix of LEDs.
|
||||
The fire appears to move upward with colors transitioning from black at the bottom
|
||||
to white at the top, with red and yellow in between (for the default palette).
|
||||
*/
|
||||
|
||||
// Perlin noise fire procedure
|
||||
// 16x16 rgb led matrix demo
|
||||
// Yaroslaw Turbin, 22.06.2020
|
||||
// https://vk.com/ldirko
|
||||
// https://www.reddit.com/user/ldirko/
|
||||
// https://www.reddit.com/r/FastLED/comments/hgu16i/my_fire_effect_implementation_based_on_perlin/
|
||||
// Based on the code found at: https://editor.soulmatelights.com/gallery/1229-
|
||||
|
||||
// HOW THE FIRE EFFECT WORKS:
|
||||
// 1. We use Perlin noise with time offset for X and Z coordinates
|
||||
// to create a naturally scrolling fire pattern that changes over time
|
||||
// 2. We distort the fire noise to make it look more realistic
|
||||
// 3. We subtract a value based on Y coordinate to shift the fire color in the palette
|
||||
// (not just brightness). This creates a fade-out effect from the bottom of the matrix to the top
|
||||
// 4. The palette is carefully designed to give realistic fire colors
|
||||
|
||||
#include "FastLED.h" // Main FastLED library for controlling LEDs
|
||||
#include "fl/ui.h" // UI components for the FastLED web compiler (sliders, etc.)
|
||||
#include "fl/xymap.h" // Mapping between 1D LED array and 2D coordinates
|
||||
#include "fx/time.h" // Time manipulation utilities
|
||||
|
||||
using namespace fl; // Use the FastLED namespace for convenience
|
||||
|
||||
// Matrix dimensions - this defines the size of our virtual LED grid
|
||||
#define HEIGHT 100 // Number of rows in the matrix
|
||||
#define WIDTH 100 // Number of columns in the matrix
|
||||
#define SERPENTINE true // Whether the LED strip zigzags back and forth (common in matrix layouts)
|
||||
#define BRIGHTNESS 255 // Maximum brightness level (0-255)
|
||||
|
||||
// TimeWarp helps control animation speed - it tracks time and allows speed adjustments
|
||||
TimeWarp timeScale(0, 1.0f); // Initialize with 0 starting time and 1.0 speed multiplier
|
||||
|
||||
// UI Controls that appear in the FastLED web compiler interface:
|
||||
UISlider scaleXY("Scale", 20, 1, 100, 1); // Controls the size of the fire pattern
|
||||
UISlider speedY("SpeedY", 1, 1, 6, .1); // Controls how fast the fire moves upward
|
||||
UISlider invSpeedZ("Inverse SpeedZ", 20, 1, 100, 1); // Controls how fast the fire pattern changes over time (higher = slower)
|
||||
UISlider brightness("Brightness", 255, 0, 255, 1); // Controls overall brightness
|
||||
UINumberField palette("Palette", 0, 0, 2); // Selects which color palette to use (0=fire, 1=green, 2=blue)
|
||||
|
||||
// Array to hold all LED color values - one CRGB struct per LED
|
||||
CRGB leds[HEIGHT * WIDTH];
|
||||
|
||||
// Color palettes define the gradient of colors used for the fire effect
|
||||
// Each entry has the format: position (0-255), R, G, B
|
||||
|
||||
DEFINE_GRADIENT_PALETTE(firepal){
|
||||
// Traditional fire palette - transitions from black to red to yellow to white
|
||||
0, 0, 0, 0, // black (bottom of fire)
|
||||
32, 255, 0, 0, // red (base of flames)
|
||||
190, 255, 255, 0, // yellow (middle of flames)
|
||||
255, 255, 255, 255 // white (hottest part/tips of flames)
|
||||
};
|
||||
|
||||
DEFINE_GRADIENT_PALETTE(electricGreenFirePal){
|
||||
// Green fire palette - for a toxic/alien look
|
||||
0, 0, 0, 0, // black (bottom)
|
||||
32, 0, 70, 0, // dark green (base)
|
||||
190, 57, 255, 20, // electric neon green (middle)
|
||||
255, 255, 255, 255 // white (hottest part)
|
||||
};
|
||||
|
||||
DEFINE_GRADIENT_PALETTE(electricBlueFirePal) {
|
||||
// Blue fire palette - for a cold/ice fire look
|
||||
0, 0, 0, 0, // Black (bottom)
|
||||
32, 0, 0, 70, // Dark blue (base)
|
||||
128, 20, 57, 255, // Electric blue (middle)
|
||||
255, 255, 255, 255 // White (hottest part)
|
||||
};
|
||||
|
||||
// Create a mapping between 1D array positions and 2D x,y coordinates
|
||||
XYMap xyMap(HEIGHT, WIDTH, SERPENTINE);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200); // Initialize serial communication for debugging
|
||||
|
||||
// Initialize the LED strip:
|
||||
// - NEOPIXEL is the LED type
|
||||
// - 3 is the data pin number (for real hardware)
|
||||
// - setScreenMap connects our 2D coordinate system to the 1D LED array
|
||||
FastLED.addLeds<NEOPIXEL, 3>(leds, HEIGHT * WIDTH).setScreenMap(xyMap);
|
||||
|
||||
// Apply color correction for more accurate colors on LED strips
|
||||
FastLED.setCorrection(TypicalLEDStrip);
|
||||
}
|
||||
|
||||
uint8_t getPaletteIndex(uint32_t millis32, int i, int j, uint32_t y_speed) {
|
||||
// This function calculates which color to use from our palette for each LED
|
||||
|
||||
// Get the scale factor from the UI slider (controls the "size" of the fire)
|
||||
uint16_t scale = scaleXY.as<uint16_t>();
|
||||
|
||||
// Calculate 3D coordinates for the Perlin noise function:
|
||||
uint16_t x = i * scale; // X position (horizontal in matrix)
|
||||
uint32_t y = j * scale + y_speed; // Y position (vertical) + movement offset
|
||||
uint16_t z = millis32 / invSpeedZ.as<uint16_t>(); // Z position (time dimension)
|
||||
|
||||
// Generate 16-bit Perlin noise value using these coordinates
|
||||
// The << 8 shifts values left by 8 bits (multiplies by 256) to use the full 16-bit range
|
||||
uint16_t noise16 = inoise16(x << 8, y << 8, z << 8);
|
||||
|
||||
// Convert 16-bit noise to 8-bit by taking the high byte (>> 8 shifts right by 8 bits)
|
||||
uint8_t noise_val = noise16 >> 8;
|
||||
|
||||
// Calculate how much to subtract based on vertical position (j)
|
||||
// This creates the fade-out effect from bottom to top
|
||||
// abs8() ensures we get a positive value
|
||||
// The formula maps j from 0 to WIDTH-1 to a value from 255 to 0
|
||||
int8_t subtraction_factor = abs8(j - (WIDTH - 1)) * 255 / (WIDTH - 1);
|
||||
|
||||
// Subtract the factor from the noise value (with underflow protection)
|
||||
// qsub8 is a "saturating subtraction" - it won't go below 0
|
||||
return qsub8(noise_val, subtraction_factor);
|
||||
}
|
||||
|
||||
CRGBPalette16 getPalette() {
|
||||
// This function returns the appropriate color palette based on the UI selection
|
||||
switch (palette) {
|
||||
case 0:
|
||||
return firepal; // Traditional orange/red fire
|
||||
case 1:
|
||||
return electricGreenFirePal; // Green "toxic" fire
|
||||
case 2:
|
||||
return electricBlueFirePal; // Blue "cold" fire
|
||||
default:
|
||||
return firepal; // Default to traditional fire if invalid value
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// The main program loop that runs continuously
|
||||
|
||||
// Set the overall brightness from the UI slider
|
||||
FastLED.setBrightness(brightness);
|
||||
|
||||
// Get the selected color palette
|
||||
CRGBPalette16 myPal = getPalette();
|
||||
|
||||
// Get the current time in milliseconds
|
||||
uint32_t now = millis();
|
||||
|
||||
// Update the animation speed from the UI slider
|
||||
timeScale.setSpeed(speedY);
|
||||
|
||||
// Calculate the current y-offset for animation (makes the fire move)
|
||||
uint32_t y_speed = timeScale.update(now);
|
||||
|
||||
// Loop through every LED in our matrix
|
||||
for (int i = 0; i < HEIGHT; i++) {
|
||||
for (int j = 0; j < WIDTH; j++) {
|
||||
// Calculate which color to use from our palette for this LED
|
||||
uint8_t palette_index = getPaletteIndex(now, i, j, y_speed);
|
||||
|
||||
// Get the actual RGB color from the palette
|
||||
// BRIGHTNESS ensures we use the full brightness range
|
||||
CRGB c = ColorFromPalette(myPal, palette_index, BRIGHTNESS);
|
||||
|
||||
// Convert our 2D coordinates (i,j) to the 1D array index
|
||||
// We use (HEIGHT-1)-i and (WIDTH-1)-j to flip the coordinates
|
||||
// This makes the fire appear to rise from the bottom
|
||||
int index = xyMap((HEIGHT - 1) - i, (WIDTH - 1) - j);
|
||||
|
||||
// Set the LED color in our array
|
||||
leds[index] = c;
|
||||
}
|
||||
}
|
||||
|
||||
// Send the color data to the actual LEDs
|
||||
FastLED.show();
|
||||
}
|
||||
96
libraries/FastLED/examples/FirstLight/FirstLight.ino
Normal file
96
libraries/FastLED/examples/FirstLight/FirstLight.ino
Normal file
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
91
libraries/FastLED/examples/FunkyClouds/FunkyClouds.ino
Normal file
91
libraries/FastLED/examples/FunkyClouds/FunkyClouds.ino
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
TODO:
|
||||
|
||||
example show oscis+p
|
||||
document caleidoscopes better
|
||||
write better caleidoscopes...
|
||||
improve and document emitters and oszillators
|
||||
explaining one example step by step:
|
||||
goal? what? how? why?
|
||||
gridmapping for rotation + zoom
|
||||
good interpolation for other matrix dimensions than 16*16
|
||||
more move & stream functions
|
||||
layers
|
||||
palettes
|
||||
link effects to areas
|
||||
1D examples
|
||||
2d example with more than 2 sines
|
||||
speed up MSGEQ7 readings
|
||||
|
||||
|
||||
DONE:
|
||||
25.6. creating basic structure
|
||||
setting up basic examples
|
||||
26.6. MSGEQ7 Support
|
||||
wrote more examples
|
||||
27.6. improved documentation
|
||||
added Move
|
||||
added AutoRun
|
||||
TODO list
|
||||
Copy
|
||||
29.6. rotate+mirror triangle
|
||||
more examples
|
||||
30.6. RenderCustomMatrix
|
||||
added more comments
|
||||
alpha version released
|
||||
|
||||
|
||||
/*
|
||||
|
||||
/*
|
||||
Funky Clouds Compendium (alpha version)
|
||||
by Stefan Petrick
|
||||
|
||||
An ever growing list of examples, tools and toys
|
||||
for creating one- and twodimensional LED effects.
|
||||
|
||||
Dedicated to the users of the FastLED v2.1 library
|
||||
by Daniel Garcia and Mark Kriegsmann.
|
||||
|
||||
Provides basic and advanced helper functions.
|
||||
Contains many examples how to creatively combine them.
|
||||
|
||||
Tested @ATmega2560 (runs propably NOT on an Uno or
|
||||
anything else with less than 4kB RAM)
|
||||
*/
|
||||
|
||||
#include "FastLED.h"
|
||||
#include "defs.h"
|
||||
#include "funky.h"
|
||||
|
||||
/*
|
||||
-------------------------------------------------------------------
|
||||
Init Inputs and Outputs: LEDs and MSGEQ7
|
||||
-------------------------------------------------------------------
|
||||
*/
|
||||
void setup() {
|
||||
// use the following line only when working with a 16*16
|
||||
// and delete everything in the function RenderCustomMatrix()
|
||||
// at the end of the code; edit XY() to change your matrix layout
|
||||
// right now it is doing a serpentine mapping
|
||||
|
||||
// just for debugging:
|
||||
// Serial.begin(9600);
|
||||
Serial.begin(38400);
|
||||
InitFunky();
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------------------------------------------------
|
||||
The main program
|
||||
-------------------------------------------------------------------
|
||||
*/
|
||||
void loop() {
|
||||
AutoRun();
|
||||
// Comment AutoRun out and test examples seperately here
|
||||
|
||||
// Dots2();
|
||||
|
||||
// For discovering parameters of examples I reccomend to
|
||||
// tinker with a renamed copy ...
|
||||
}
|
||||
33
libraries/FastLED/examples/FunkyClouds/defs.h
Normal file
33
libraries/FastLED/examples/FunkyClouds/defs.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
// define your LED hardware setup here
|
||||
#define CHIPSET WS2812B
|
||||
#define LED_PIN 13
|
||||
#define COLOR_ORDER GRB
|
||||
|
||||
// set master brightness 0-255 here to adjust power consumption
|
||||
// and light intensity
|
||||
#define BRIGHTNESS 60
|
||||
|
||||
// enter your custom matrix size if it is NOT a 16*16 and
|
||||
// check in that case the setup part and
|
||||
// RenderCustomMatrix() and
|
||||
// ShowFrame() for more comments
|
||||
#define CUSTOM_WIDTH 8
|
||||
#define CUSTOM_HEIGHT 8
|
||||
|
||||
// MSGEQ7 wiring on spectrum analyser shield
|
||||
#define MSGEQ7_STROBE_PIN 4
|
||||
#define MSGEQ7_RESET_PIN 5
|
||||
#define AUDIO_LEFT_PIN 0
|
||||
#define AUDIO_RIGHT_PIN 1
|
||||
|
||||
|
||||
// all 2D effects will be calculated in this matrix size
|
||||
// do not touch
|
||||
#define WIDTH 16
|
||||
#define HEIGHT 16
|
||||
|
||||
// number of LEDs based on fixed calculation matrix size
|
||||
// do not touch
|
||||
#define NUM_LEDS (WIDTH * HEIGHT)
|
||||
1310
libraries/FastLED/examples/FunkyClouds/funky.cpp
Normal file
1310
libraries/FastLED/examples/FunkyClouds/funky.cpp
Normal file
File diff suppressed because it is too large
Load diff
60
libraries/FastLED/examples/FunkyClouds/funky.h
Normal file
60
libraries/FastLED/examples/FunkyClouds/funky.h
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
#pragma once
|
||||
|
||||
/*
|
||||
TODO:
|
||||
|
||||
example show oscis+p
|
||||
document caleidoscopes better
|
||||
write better caleidoscopes...
|
||||
improve and document emitters and oszillators
|
||||
explaining one example step by step:
|
||||
goal? what? how? why?
|
||||
gridmapping for rotation + zoom
|
||||
good interpolation for other matrix dimensions than 16*16
|
||||
more move & stream functions
|
||||
layers
|
||||
palettes
|
||||
link effects to areas
|
||||
1D examples
|
||||
2d example with more than 2 sines
|
||||
speed up MSGEQ7 readings
|
||||
|
||||
|
||||
DONE:
|
||||
25.6. creating basic structure
|
||||
setting up basic examples
|
||||
26.6. MSGEQ7 Support
|
||||
wrote more examples
|
||||
27.6. improved documentation
|
||||
added Move
|
||||
added AutoRun
|
||||
TODO list
|
||||
Copy
|
||||
29.6. rotate+mirror triangle
|
||||
more examples
|
||||
30.6. RenderCustomMatrix
|
||||
added more comments
|
||||
alpha version released
|
||||
|
||||
|
||||
/*
|
||||
|
||||
/*
|
||||
Funky Clouds Compendium (alpha version)
|
||||
by Stefan Petrick
|
||||
|
||||
An ever growing list of examples, tools and toys
|
||||
for creating one- and twodimensional LED effects.
|
||||
|
||||
Dedicated to the users of the FastLED v2.1 library
|
||||
by Daniel Garcia and Mark Kriegsmann.
|
||||
|
||||
Provides basic and advanced helper functions.
|
||||
Contains many examples how to creatively combine them.
|
||||
|
||||
Tested @ATmega2560 (runs propably NOT on an Uno or
|
||||
anything else with less than 4kB RAM)
|
||||
*/
|
||||
|
||||
void InitFunky();
|
||||
void AutoRun();
|
||||
26
libraries/FastLED/examples/FunkyClouds/gfx.cpp
Normal file
26
libraries/FastLED/examples/FunkyClouds/gfx.cpp
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
|
||||
#include "gfx.h"
|
||||
#include "FastLED.h"
|
||||
|
||||
// the rendering buffer (16*16)
|
||||
// do not touch
|
||||
CRGB leds[NUM_LEDS];
|
||||
XYMap xyMap = XYMap::constructRectangularGrid(CUSTOM_WIDTH, CUSTOM_HEIGHT);
|
||||
|
||||
void InitGraphics() {
|
||||
// set the brightness
|
||||
auto* controller = &FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds + 5, NUM_LEDS - 5);
|
||||
fl::ScreenMap screenMap = xyMap.toScreenMap();
|
||||
controller->setScreenMap(screenMap);
|
||||
// use this line only when using a custom size matrix
|
||||
// FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds2, CUSTOM_HEIGHT *
|
||||
// CUSTOM_WIDTH);
|
||||
FastLED.setBrightness(BRIGHTNESS);
|
||||
FastLED.setDither(0);
|
||||
}
|
||||
|
||||
void GraphicsShow() {
|
||||
// when using a matrix different than 16*16 use
|
||||
// RenderCustomMatrix();
|
||||
FastLED.show();
|
||||
}
|
||||
10
libraries/FastLED/examples/FunkyClouds/gfx.h
Normal file
10
libraries/FastLED/examples/FunkyClouds/gfx.h
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "defs.h"
|
||||
#include "crgb.h"
|
||||
|
||||
extern CRGB leds[NUM_LEDS];
|
||||
|
||||
void InitGraphics();
|
||||
void GraphicsShow();
|
||||
74
libraries/FastLED/examples/FxAnimartrix/FxAnimartrix.ino
Normal file
74
libraries/FastLED/examples/FxAnimartrix/FxAnimartrix.ino
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/// @file Animartrix.ino
|
||||
/// @brief Demonstrates Stefan Petricks Animartrix effects.
|
||||
/// @author Stefan Petrick
|
||||
/// @author Zach Vorhies (FastLED adaptation)
|
||||
///
|
||||
/// This sketch is fully compatible with the FastLED web compiler. To use it do the following:
|
||||
/// 1. Install Fastled: `pip install fastled`
|
||||
/// 2. cd into this examples page.
|
||||
/// 3. Run the FastLED web compiler at root: `fastled`
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
#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 96
|
||||
#define COLOR_ORDER GRB
|
||||
|
||||
#define MATRIX_WIDTH 32
|
||||
#define MATRIX_HEIGHT 32
|
||||
|
||||
#define NUM_LEDS (MATRIX_WIDTH * MATRIX_HEIGHT)
|
||||
|
||||
#define FIRST_ANIMATION POLAR_WAVES
|
||||
|
||||
|
||||
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", 255, 0, 255);
|
||||
UINumberField fxIndex("Animartrix - index", 0, 0, NUM_ANIMATIONS - 1);
|
||||
UISlider timeSpeed("Time Speed", 1, -10, 10, .1);
|
||||
|
||||
Animartrix animartrix(xyMap, FIRST_ANIMATION);
|
||||
FxEngine fxEngine(NUM_LEDS);
|
||||
|
||||
void setup() {
|
||||
auto screen_map = xyMap.toScreenMap();
|
||||
screen_map.setDiameter(.1);
|
||||
FastLED.addLeds<WS2811, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS)
|
||||
.setCorrection(TypicalLEDStrip)
|
||||
.setScreenMap(screen_map);
|
||||
FastLED.setBrightness(brightness);
|
||||
fxEngine.addFx(animartrix);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
FastLED.setBrightness(brightness);
|
||||
fxEngine.setSpeed(timeSpeed);
|
||||
static int lastFxIndex = -1;
|
||||
if (fxIndex.value() != lastFxIndex) {
|
||||
lastFxIndex = fxIndex;
|
||||
animartrix.fxSet(fxIndex);
|
||||
}
|
||||
fxEngine.draw(millis(), leds);
|
||||
FastLED.show();
|
||||
}
|
||||
|
||||
|
||||
36
libraries/FastLED/examples/FxCylon/FxCylon.ino
Normal file
36
libraries/FastLED/examples/FxCylon/FxCylon.ino
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/// @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>
|
||||
#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);
|
||||
}
|
||||
64
libraries/FastLED/examples/FxDemoReel100/FxDemoReel100.ino
Normal file
64
libraries/FastLED/examples/FxDemoReel100/FxDemoReel100.ino
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/// @file DemoReel100.ino
|
||||
/// @brief FastLED "100 lines of code" demo reel, showing off some effects
|
||||
/// @example DemoReel100.ino
|
||||
|
||||
#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 = DemoReel100Ptr::New(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
|
||||
8
libraries/FastLED/examples/FxDemoReel100/defs.h
Normal file
8
libraries/FastLED/examples/FxDemoReel100/defs.h
Normal file
|
|
@ -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
|
||||
75
libraries/FastLED/examples/FxEngine/FxEngine.ino
Normal file
75
libraries/FastLED/examples/FxEngine/FxEngine.ino
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/// @file FxEngine.ino
|
||||
/// @brief Demonstrates how to use the FxEngine to switch between different effects on a 2D LED matrix.
|
||||
/// This example is compatible with the new FastLED wasm compiler. Install it by running
|
||||
/// `pip install fastled` then running `fastled` in this sketch directory.
|
||||
/// @example FxEngine.ino
|
||||
|
||||
#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__
|
||||
75
libraries/FastLED/examples/FxFire2012/FxFire2012.ino
Normal file
75
libraries/FastLED/examples/FxFire2012/FxFire2012.ino
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
|
||||
/// @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 = Fire2012Ptr::New(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);
|
||||
}
|
||||
|
||||
94
libraries/FastLED/examples/FxGfx2Video/FxGfx2Video.ino
Normal file
94
libraries/FastLED/examples/FxGfx2Video/FxGfx2Video.ino
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
/// @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/ptr.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;
|
||||
}
|
||||
}
|
||||
|
||||
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 = ByteStreamMemoryPtr::New(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,102 @@
|
|||
/// @file NoisePlusPalette.ino
|
||||
/// @brief Demonstrates how to mix noise generation with color palettes on a
|
||||
/// 2D LED matrix
|
||||
/// @example NoisePlusPalette.ino
|
||||
|
||||
#ifndef COMPILE_NOISEPLUSPALETTE
|
||||
#if defined(__AVR__)
|
||||
// This has grown too large for the AVR to handle.
|
||||
#define COMPILE_NOISEPLUSPALETTE 0
|
||||
#else
|
||||
#define COMPILE_NOISEPLUSPALETTE 1
|
||||
#endif
|
||||
#endif // COMPILE_NOISEPLUSPALETTE
|
||||
|
||||
#if COMPILE_NOISEPLUSPALETTE
|
||||
|
||||
#include <FastLED.h>
|
||||
#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();
|
||||
}
|
||||
|
||||
#else
|
||||
void setup() {}
|
||||
void loop() {}
|
||||
#endif // COMPILE_NOISEPLUSPALETTE
|
||||
108
libraries/FastLED/examples/FxNoiseRing/FxNoiseRing.ino
Normal file
108
libraries/FastLED/examples/FxNoiseRing/FxNoiseRing.ino
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
|
||||
|
||||
/// @file NoiseRing.ino
|
||||
/// @brief Shows how to use a circular noise generator to have a continuous noise effect on a ring of LEDs.
|
||||
/// @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 <Arduino.h>
|
||||
|
||||
#include <stdio.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 "FastLED.h"
|
||||
#include "sensors/pir.h"
|
||||
#include "timer.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];
|
||||
|
||||
// These sliders and checkboxes are dynamic when using the FastLED web compiler.
|
||||
// When deployed to a real device these elements will always be the default value.
|
||||
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);
|
||||
// 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.
|
||||
}
|
||||
|
||||
void draw(uint32_t now) {
|
||||
double angle_offset = double(now) / 32000.0 * 2 * M_PI;
|
||||
now = (now << timeBitshift.as<int>()) * timescale.as<double>();
|
||||
// go in circular formation and set the leds
|
||||
for (int i = 0; i < NUM_LEDS; i++) {
|
||||
// Sorry this is a little convoluted but we are applying noise
|
||||
// in the 16-bit space and then mapping it back to 8-bit space.
|
||||
// All the constants were experimentally determined.
|
||||
float angle = i * 2 * M_PI / NUM_LEDS + angle_offset;
|
||||
float x = cos(angle);
|
||||
float y = sin(angle);
|
||||
x *= 0xffff * scale.as<double>();
|
||||
y *= 0xffff * scale.as<double>();
|
||||
uint16_t noise = inoise16(x, y, now);
|
||||
uint16_t noise2 = inoise16(x, y, 0xfff + now);
|
||||
uint16_t noise3 = inoise16(x, y, 0xffff + now);
|
||||
noise3 = noise3 >> 8;
|
||||
int16_t noise4 = map(noise3, 0, 255, -64, 255);
|
||||
if (noise4 < 0) { // Clamp negative values to 0.
|
||||
noise4 = 0;
|
||||
}
|
||||
// Shift back to 8-bit space.
|
||||
leds[i] = CHSV(noise >> 8, MAX(128, noise2 >> 8), noise4);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
58
libraries/FastLED/examples/FxNoiseRing/timer.h
Normal file
58
libraries/FastLED/examples/FxNoiseRing/timer.h
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
#pragma once
|
||||
|
||||
#include <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
|
||||
};
|
||||
52
libraries/FastLED/examples/FxPacifica/FxPacifica.ino
Normal file
52
libraries/FastLED/examples/FxPacifica/FxPacifica.ino
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/// @file Pacifica.ino
|
||||
/// @brief Gentle, blue-green ocean wave animation
|
||||
/// @example Pacifica.ino
|
||||
|
||||
//
|
||||
// "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
libraries/FastLED/examples/FxPacifica/defs.h
Normal file
9
libraries/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
|
||||
32
libraries/FastLED/examples/FxPride2015/FxPride2015.ino
Normal file
32
libraries/FastLED/examples/FxPride2015/FxPride2015.ino
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#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();
|
||||
}
|
||||
135
libraries/FastLED/examples/FxSdCard/FxSdCard.ino
Normal file
135
libraries/FastLED/examples/FxSdCard/FxSdCard.ino
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
/// @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.
|
||||
|
||||
|
||||
#ifdef __AVR__
|
||||
void setup() {
|
||||
// put your setup code here, to run once:
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// put your main code here, to run repeatedly:
|
||||
}
|
||||
#else
|
||||
|
||||
#include "FastLED.h"
|
||||
#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
|
||||
BIN
libraries/FastLED/examples/FxSdCard/data/color_line_bubbles.rgb
Normal file
BIN
libraries/FastLED/examples/FxSdCard/data/color_line_bubbles.rgb
Normal file
Binary file not shown.
4
libraries/FastLED/examples/FxSdCard/data/readme.txt
Normal file
4
libraries/FastLED/examples/FxSdCard/data/readme.txt
Normal file
|
|
@ -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"
|
||||
2
libraries/FastLED/examples/FxSdCard/data/screenmap.json
Normal file
2
libraries/FastLED/examples/FxSdCard/data/screenmap.json
Normal file
File diff suppressed because one or more lines are too long
BIN
libraries/FastLED/examples/FxSdCard/data/video.rgb
Normal file
BIN
libraries/FastLED/examples/FxSdCard/data/video.rgb
Normal file
Binary file not shown.
30
libraries/FastLED/examples/FxTwinkleFox/FxTwinkleFox.ino
Normal file
30
libraries/FastLED/examples/FxTwinkleFox/FxTwinkleFox.ino
Normal file
|
|
@ -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();
|
||||
}
|
||||
149
libraries/FastLED/examples/FxWater/FxWater.ino
Normal file
149
libraries/FastLED/examples/FxWater/FxWater.ino
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
// 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);
|
||||
}
|
||||
}
|
||||
33
libraries/FastLED/examples/FxWave2d/FxWave2d.ino
Normal file
33
libraries/FastLED/examples/FxWave2d/FxWave2d.ino
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
|
||||
|
||||
/*
|
||||
This demo is best viewed using the FastLED compiler.
|
||||
Install: pip install fastled
|
||||
Run: fastled <this sketch directory>
|
||||
This will compile and preview the sketch in the browser, and enable
|
||||
all the UI elements you see below.
|
||||
|
||||
OVERVIEW:
|
||||
This sketch 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 "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();
|
||||
}
|
||||
400
libraries/FastLED/examples/FxWave2d/wavefx.cpp
Normal file
400
libraries/FastLED/examples/FxWave2d/wavefx.cpp
Normal file
|
|
@ -0,0 +1,400 @@
|
|||
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
/*
|
||||
This demo is best viewed using the FastLED compiler.
|
||||
Install: pip install fastled
|
||||
Run: fastled <this sketch directory>
|
||||
This will compile and preview the sketch in the browser, and enable
|
||||
all the UI elements you see below.
|
||||
|
||||
OVERVIEW:
|
||||
This sketch 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/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.");
|
||||
|
||||
// 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)
|
||||
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)
|
||||
|
||||
UICheckbox xCyclical("X Is Cyclical", false); // If true, waves wrap around the x-axis (like a loop)
|
||||
|
||||
// Upper wave layer controls:
|
||||
UISlider speedUpper("Wave Upper: Speed", 0.12f, 0.0f, 1.0f); // How fast the upper wave propagates
|
||||
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
|
||||
|
||||
// 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 = WaveCrgbGradientMapPtr::New(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 = WaveCrgbGradientMapPtr::New(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
|
||||
|
||||
// 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
|
||||
|
||||
// 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
|
||||
Blend2dParams lower_params = {
|
||||
.blur_amount = blurAmountLower, // Blur amount for lower layer
|
||||
.blur_passes = blurPassesLower, // Blur passes for lower layer
|
||||
};
|
||||
|
||||
Blend2dParams upper_params = {
|
||||
.blur_amount = blurAmountUpper, // Blur amount for upper layer
|
||||
.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{
|
||||
.button = button, // Regular ripple button
|
||||
.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);
|
||||
|
||||
// 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();
|
||||
}
|
||||
28
libraries/FastLED/examples/FxWave2d/wavefx.h
Normal file
28
libraries/FastLED/examples/FxWave2d/wavefx.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
/*
|
||||
This demo is best viewed using the FastLED compiler.
|
||||
Install: pip install fastled
|
||||
Run: fastled <this sketch directory>
|
||||
This will compile and preview the sketch in the browser, and enable
|
||||
all the UI elements you see below.
|
||||
|
||||
OVERVIEW:
|
||||
This sketch 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
libraries/FastLED/examples/HD107/HD107.ino
Normal file
63
libraries/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.
|
||||
}
|
||||
254
libraries/FastLED/examples/LuminescentGrand/LuminescentGrand.ino
Normal file
254
libraries/FastLED/examples/LuminescentGrand/LuminescentGrand.ino
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
|
||||
/// 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) {
|
||||
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) {
|
||||
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) {
|
||||
keyboard.HandleAfterTouchPoly(note, pressure);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
// Detects whether the foot pedal has been touched.
|
||||
void HandleControlChange(byte channel, byte d1, byte d2) {
|
||||
keyboard.HandleControlChange(d1, d2);
|
||||
}
|
||||
|
||||
void HandleAfterTouchChannel(byte channel, byte 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();
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
// 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.
|
||||
|
||||
#ifndef LED_REPE_TCL_H_
|
||||
#define LED_REPE_TCL_H_
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "../shared/color.h"
|
||||
#include "../shared/framebuffer.h"
|
||||
#include "../shared/led_rope_interface.h"
|
||||
|
||||
#include "fl/vector.h"
|
||||
#include "crgb.h"
|
||||
#include "fl/screenmap.h"
|
||||
|
||||
using namespace fl;
|
||||
|
||||
// LedRopeTCL is a C++ wrapper around the Total Control Lighting LED rope
|
||||
// device driver (TCL.h). This wrapper includes automatic setup of the LED
|
||||
// rope and allows the user to use a graphics-state like interface for
|
||||
// talking to the rope. A copy of the rope led state is held in this class
|
||||
// which makes blending operations easier. After all changes by the user
|
||||
// are applied to the rope, the hardware is updated via an explicit Draw()
|
||||
// command.
|
||||
//
|
||||
// Whole-rope blink Example:
|
||||
// #include <SPI.h>
|
||||
// #include <TCL.h> // From CoolNeon (https://bitbucket.org/devries/arduino-tcl)
|
||||
// #include "LedRopeTCL.h"
|
||||
// LedRopeTCL led_rope(100); // 100 led-strand.
|
||||
//
|
||||
// void setup() {} // No setup necessary for Led rope.
|
||||
// void loop() {
|
||||
// led_rope.FillColor(LedRopeTCL::Color3i::Black());
|
||||
// led_rope.Draw();
|
||||
// delay(1000);
|
||||
// led_rope.FillColor(LedRopeTCL::Color3i::White());
|
||||
// led_rope.Draw();
|
||||
// delay(1000);
|
||||
// }
|
||||
|
||||
|
||||
class LedRopeTCL : public LedRopeInterface {
|
||||
public:
|
||||
LedRopeTCL(int n_pixels);
|
||||
virtual ~LedRopeTCL();
|
||||
|
||||
void Draw();
|
||||
void DrawSequentialRepeat(int repeat);
|
||||
void DrawRepeat(const int* value_array, int array_length);
|
||||
void set_draw_offset(int val);
|
||||
|
||||
virtual void Set(int i, const Color3i& c) {
|
||||
frame_buffer_.Set(i, c);
|
||||
}
|
||||
|
||||
Color3i* GetIterator(int i) {
|
||||
return frame_buffer_.GetIterator(i);
|
||||
}
|
||||
|
||||
int length() const { return frame_buffer_.length(); }
|
||||
|
||||
void RawBeginDraw();
|
||||
void RawDrawPixel(const Color3i& c);
|
||||
void RawDrawPixels(const Color3i& c, int n);
|
||||
void RawDrawPixel(uint8_t r, uint8_t g, uint8_t b);
|
||||
void RawCommitDraw();
|
||||
|
||||
protected:
|
||||
void PreDrawSetup();
|
||||
int draw_offset_ = 0;
|
||||
bool lazy_initialized_;
|
||||
FrameBuffer frame_buffer_;
|
||||
bool controller_added_ = false;
|
||||
fl::HeapVector<CRGB> led_buffer_;
|
||||
fl::ScreenMap mScreenMap;
|
||||
};
|
||||
|
||||
#endif // LED_REPE_TCL_H_
|
||||
115
libraries/FastLED/examples/LuminescentGrand/arduino/MIDI.cpp
Normal file
115
libraries/FastLED/examples/LuminescentGrand/arduino/MIDI.cpp
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
/*!
|
||||
* @file MIDI.cpp
|
||||
* Project Arduino MIDI Library
|
||||
* @brief MIDI Library for the Arduino
|
||||
* @author Francois Best
|
||||
* @date 24/02/11
|
||||
* @license MIT - Copyright (c) 2015 Francois Best
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "MIDI.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
BEGIN_MIDI_NAMESPACE
|
||||
|
||||
/*! \brief Encode System Exclusive messages.
|
||||
SysEx messages are encoded to guarantee transmission of data bytes higher than
|
||||
127 without breaking the MIDI protocol. Use this static method to convert the
|
||||
data you want to send.
|
||||
\param inData The data to encode.
|
||||
\param outSysEx The output buffer where to store the encoded message.
|
||||
\param inLength The length of the input buffer.
|
||||
\param inFlipHeaderBits True for Korg and other who store MSB in reverse order
|
||||
\return The length of the encoded output buffer.
|
||||
@see decodeSysEx
|
||||
Code inspired from Ruin & Wesen's SysEx encoder/decoder - http://ruinwesen.com
|
||||
*/
|
||||
unsigned encodeSysEx(const byte* inData,
|
||||
byte* outSysEx,
|
||||
unsigned inLength,
|
||||
bool inFlipHeaderBits)
|
||||
{
|
||||
unsigned outLength = 0; // Num bytes in output array.
|
||||
byte count = 0; // Num 7bytes in a block.
|
||||
outSysEx[0] = 0;
|
||||
|
||||
for (unsigned i = 0; i < inLength; ++i)
|
||||
{
|
||||
const byte data = inData[i];
|
||||
const byte msb = data >> 7;
|
||||
const byte body = data & 0x7f;
|
||||
|
||||
outSysEx[0] |= (msb << (inFlipHeaderBits ? count : (6 - count)));
|
||||
outSysEx[1 + count] = body;
|
||||
|
||||
if (count++ == 6)
|
||||
{
|
||||
outSysEx += 8;
|
||||
outLength += 8;
|
||||
outSysEx[0] = 0;
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
return outLength + count + (count != 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
/*! \brief Decode System Exclusive messages.
|
||||
SysEx messages are encoded to guarantee transmission of data bytes higher than
|
||||
127 without breaking the MIDI protocol. Use this static method to reassemble
|
||||
your received message.
|
||||
\param inSysEx The SysEx data received from MIDI in.
|
||||
\param outData The output buffer where to store the decrypted message.
|
||||
\param inLength The length of the input buffer.
|
||||
\param inFlipHeaderBits True for Korg and other who store MSB in reverse order
|
||||
\return The length of the output buffer.
|
||||
@see encodeSysEx @see getSysExArrayLength
|
||||
Code inspired from Ruin & Wesen's SysEx encoder/decoder - http://ruinwesen.com
|
||||
*/
|
||||
unsigned decodeSysEx(const byte* inSysEx,
|
||||
byte* outData,
|
||||
unsigned inLength,
|
||||
bool inFlipHeaderBits)
|
||||
{
|
||||
unsigned count = 0;
|
||||
byte msbStorage = 0;
|
||||
byte byteIndex = 0;
|
||||
|
||||
for (unsigned i = 0; i < inLength; ++i)
|
||||
{
|
||||
if ((i % 8) == 0)
|
||||
{
|
||||
msbStorage = inSysEx[i];
|
||||
byteIndex = 6;
|
||||
}
|
||||
else
|
||||
{
|
||||
const byte body = inSysEx[i];
|
||||
const byte shift = inFlipHeaderBits ? 6 - byteIndex : byteIndex;
|
||||
const byte msb = byte(((msbStorage >> shift) & 1) << 7);
|
||||
byteIndex--;
|
||||
outData[count++] = msb | body;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
END_MIDI_NAMESPACE
|
||||
308
libraries/FastLED/examples/LuminescentGrand/arduino/MIDI.h
Normal file
308
libraries/FastLED/examples/LuminescentGrand/arduino/MIDI.h
Normal file
|
|
@ -0,0 +1,308 @@
|
|||
/*!
|
||||
* @file MIDI.h
|
||||
* Project Arduino MIDI Library
|
||||
* @brief MIDI Library for the Arduino
|
||||
* @author Francois Best, lathoub
|
||||
* @date 24/02/11
|
||||
* @license MIT - Copyright (c) 2015 Francois Best
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "midi_Defs.h"
|
||||
#include "midi_Platform.h"
|
||||
#include "midi_Settings.h"
|
||||
#include "midi_Message.h"
|
||||
|
||||
#include "serialMIDI.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
BEGIN_MIDI_NAMESPACE
|
||||
|
||||
#define MIDI_LIBRARY_VERSION 0x050000
|
||||
#define MIDI_LIBRARY_VERSION_MAJOR 5
|
||||
#define MIDI_LIBRARY_VERSION_MINOR 0
|
||||
#define MIDI_LIBRARY_VERSION_PATCH 0
|
||||
|
||||
|
||||
/*! \brief The main class for MIDI handling.
|
||||
It is templated over the type of serial port to provide abstraction from
|
||||
the hardware interface, meaning you can use HardwareSerial, SoftwareSerial
|
||||
or ak47's Uart classes. The only requirement is that the class implements
|
||||
the begin, read, write and available methods.
|
||||
*/
|
||||
template<class Transport, class _Settings = DefaultSettings, class _Platform = DefaultPlatform>
|
||||
class MidiInterface
|
||||
{
|
||||
public:
|
||||
typedef _Settings Settings;
|
||||
typedef _Platform Platform;
|
||||
typedef Message<Settings::SysExMaxSize> MidiMessage;
|
||||
|
||||
public:
|
||||
inline MidiInterface(Transport&);
|
||||
inline ~MidiInterface();
|
||||
|
||||
public:
|
||||
void begin(Channel inChannel = 1);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// MIDI Output
|
||||
|
||||
public:
|
||||
inline void sendNoteOn(DataByte inNoteNumber,
|
||||
DataByte inVelocity,
|
||||
Channel inChannel);
|
||||
|
||||
inline void sendNoteOff(DataByte inNoteNumber,
|
||||
DataByte inVelocity,
|
||||
Channel inChannel);
|
||||
|
||||
inline void sendProgramChange(DataByte inProgramNumber,
|
||||
Channel inChannel);
|
||||
|
||||
inline void sendControlChange(DataByte inControlNumber,
|
||||
DataByte inControlValue,
|
||||
Channel inChannel);
|
||||
|
||||
inline void sendPitchBend(int inPitchValue, Channel inChannel);
|
||||
inline void sendPitchBend(double inPitchValue, Channel inChannel);
|
||||
|
||||
inline void sendPolyPressure(DataByte inNoteNumber,
|
||||
DataByte inPressure,
|
||||
Channel inChannel) __attribute__ ((deprecated));
|
||||
|
||||
inline void sendAfterTouch(DataByte inPressure,
|
||||
Channel inChannel);
|
||||
inline void sendAfterTouch(DataByte inNoteNumber,
|
||||
DataByte inPressure,
|
||||
Channel inChannel);
|
||||
|
||||
inline void sendSysEx(unsigned inLength,
|
||||
const byte* inArray,
|
||||
bool inArrayContainsBoundaries = false);
|
||||
|
||||
inline void sendTimeCodeQuarterFrame(DataByte inTypeNibble,
|
||||
DataByte inValuesNibble);
|
||||
inline void sendTimeCodeQuarterFrame(DataByte inData);
|
||||
|
||||
inline void sendSongPosition(unsigned inBeats);
|
||||
inline void sendSongSelect(DataByte inSongNumber);
|
||||
inline void sendTuneRequest();
|
||||
|
||||
inline void sendCommon(MidiType inType, unsigned = 0);
|
||||
|
||||
inline void sendClock() { sendRealTime(Clock); };
|
||||
inline void sendStart() { sendRealTime(Start); };
|
||||
inline void sendStop() { sendRealTime(Stop); };
|
||||
inline void sendTick() { sendRealTime(Tick); };
|
||||
inline void sendContinue() { sendRealTime(Continue); };
|
||||
inline void sendActiveSensing() { sendRealTime(ActiveSensing); };
|
||||
inline void sendSystemReset() { sendRealTime(SystemReset); };
|
||||
|
||||
inline void sendRealTime(MidiType inType);
|
||||
|
||||
inline void beginRpn(unsigned inNumber,
|
||||
Channel inChannel);
|
||||
inline void sendRpnValue(unsigned inValue,
|
||||
Channel inChannel);
|
||||
inline void sendRpnValue(byte inMsb,
|
||||
byte inLsb,
|
||||
Channel inChannel);
|
||||
inline void sendRpnIncrement(byte inAmount,
|
||||
Channel inChannel);
|
||||
inline void sendRpnDecrement(byte inAmount,
|
||||
Channel inChannel);
|
||||
inline void endRpn(Channel inChannel);
|
||||
|
||||
inline void beginNrpn(unsigned inNumber,
|
||||
Channel inChannel);
|
||||
inline void sendNrpnValue(unsigned inValue,
|
||||
Channel inChannel);
|
||||
inline void sendNrpnValue(byte inMsb,
|
||||
byte inLsb,
|
||||
Channel inChannel);
|
||||
inline void sendNrpnIncrement(byte inAmount,
|
||||
Channel inChannel);
|
||||
inline void sendNrpnDecrement(byte inAmount,
|
||||
Channel inChannel);
|
||||
inline void endNrpn(Channel inChannel);
|
||||
|
||||
inline void send(const MidiMessage&);
|
||||
|
||||
public:
|
||||
void send(MidiType inType,
|
||||
DataByte inData1,
|
||||
DataByte inData2,
|
||||
Channel inChannel);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// MIDI Input
|
||||
|
||||
public:
|
||||
inline bool read();
|
||||
inline bool read(Channel inChannel);
|
||||
|
||||
public:
|
||||
inline MidiType getType() const;
|
||||
inline Channel getChannel() const;
|
||||
inline DataByte getData1() const;
|
||||
inline DataByte getData2() const;
|
||||
inline const byte* getSysExArray() const;
|
||||
inline unsigned getSysExArrayLength() const;
|
||||
inline bool check() const;
|
||||
|
||||
public:
|
||||
inline Channel getInputChannel() const;
|
||||
inline void setInputChannel(Channel inChannel);
|
||||
|
||||
public:
|
||||
static inline MidiType getTypeFromStatusByte(byte inStatus);
|
||||
static inline Channel getChannelFromStatusByte(byte inStatus);
|
||||
static inline bool isChannelMessage(MidiType inType);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Input Callbacks
|
||||
|
||||
public:
|
||||
inline void setHandleMessage(void (*fptr)(const MidiMessage&)) { mMessageCallback = fptr; };
|
||||
inline void setHandleError(ErrorCallback fptr) { mErrorCallback = fptr; }
|
||||
inline void setHandleNoteOff(NoteOffCallback fptr) { mNoteOffCallback = fptr; }
|
||||
inline void setHandleNoteOn(NoteOnCallback fptr) { mNoteOnCallback = fptr; }
|
||||
inline void setHandleAfterTouchPoly(AfterTouchPolyCallback fptr) { mAfterTouchPolyCallback = fptr; }
|
||||
inline void setHandleControlChange(ControlChangeCallback fptr) { mControlChangeCallback = fptr; }
|
||||
inline void setHandleProgramChange(ProgramChangeCallback fptr) { mProgramChangeCallback = fptr; }
|
||||
inline void setHandleAfterTouchChannel(AfterTouchChannelCallback fptr) { mAfterTouchChannelCallback = fptr; }
|
||||
inline void setHandlePitchBend(PitchBendCallback fptr) { mPitchBendCallback = fptr; }
|
||||
inline void setHandleSystemExclusive(SystemExclusiveCallback fptr) { mSystemExclusiveCallback = fptr; }
|
||||
inline void setHandleTimeCodeQuarterFrame(TimeCodeQuarterFrameCallback fptr) { mTimeCodeQuarterFrameCallback = fptr; }
|
||||
inline void setHandleSongPosition(SongPositionCallback fptr) { mSongPositionCallback = fptr; }
|
||||
inline void setHandleSongSelect(SongSelectCallback fptr) { mSongSelectCallback = fptr; }
|
||||
inline void setHandleTuneRequest(TuneRequestCallback fptr) { mTuneRequestCallback = fptr; }
|
||||
inline void setHandleClock(ClockCallback fptr) { mClockCallback = fptr; }
|
||||
inline void setHandleStart(StartCallback fptr) { mStartCallback = fptr; }
|
||||
inline void setHandleTick(TickCallback fptr) { mTickCallback = fptr; }
|
||||
inline void setHandleContinue(ContinueCallback fptr) { mContinueCallback = fptr; }
|
||||
inline void setHandleStop(StopCallback fptr) { mStopCallback = fptr; }
|
||||
inline void setHandleActiveSensing(ActiveSensingCallback fptr) { mActiveSensingCallback = fptr; }
|
||||
inline void setHandleSystemReset(SystemResetCallback fptr) { mSystemResetCallback = fptr; }
|
||||
|
||||
inline void disconnectCallbackFromType(MidiType inType);
|
||||
|
||||
private:
|
||||
void launchCallback();
|
||||
|
||||
void (*mMessageCallback)(const MidiMessage& message) = nullptr;
|
||||
ErrorCallback mErrorCallback = nullptr;
|
||||
NoteOffCallback mNoteOffCallback = nullptr;
|
||||
NoteOnCallback mNoteOnCallback = nullptr;
|
||||
AfterTouchPolyCallback mAfterTouchPolyCallback = nullptr;
|
||||
ControlChangeCallback mControlChangeCallback = nullptr;
|
||||
ProgramChangeCallback mProgramChangeCallback = nullptr;
|
||||
AfterTouchChannelCallback mAfterTouchChannelCallback = nullptr;
|
||||
PitchBendCallback mPitchBendCallback = nullptr;
|
||||
SystemExclusiveCallback mSystemExclusiveCallback = nullptr;
|
||||
TimeCodeQuarterFrameCallback mTimeCodeQuarterFrameCallback = nullptr;
|
||||
SongPositionCallback mSongPositionCallback = nullptr;
|
||||
SongSelectCallback mSongSelectCallback = nullptr;
|
||||
TuneRequestCallback mTuneRequestCallback = nullptr;
|
||||
ClockCallback mClockCallback = nullptr;
|
||||
StartCallback mStartCallback = nullptr;
|
||||
TickCallback mTickCallback = nullptr;
|
||||
ContinueCallback mContinueCallback = nullptr;
|
||||
StopCallback mStopCallback = nullptr;
|
||||
ActiveSensingCallback mActiveSensingCallback = nullptr;
|
||||
SystemResetCallback mSystemResetCallback = nullptr;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// MIDI Soft Thru
|
||||
|
||||
public:
|
||||
inline Thru::Mode getFilterMode() const;
|
||||
inline bool getThruState() const;
|
||||
|
||||
inline void turnThruOn(Thru::Mode inThruFilterMode = Thru::Full);
|
||||
inline void turnThruOff();
|
||||
inline void setThruFilterMode(Thru::Mode inThruFilterMode);
|
||||
|
||||
private:
|
||||
void thruFilter(byte inChannel);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// MIDI Parsing
|
||||
|
||||
private:
|
||||
bool parse();
|
||||
inline void handleNullVelocityNoteOnAsNoteOff();
|
||||
inline bool inputFilter(Channel inChannel);
|
||||
inline void resetInput();
|
||||
inline void UpdateLastSentTime();
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Transport
|
||||
|
||||
public:
|
||||
Transport* getTransport() { return &mTransport; };
|
||||
|
||||
private:
|
||||
Transport& mTransport;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Internal variables
|
||||
|
||||
private:
|
||||
Channel mInputChannel;
|
||||
StatusByte mRunningStatus_RX;
|
||||
StatusByte mRunningStatus_TX;
|
||||
byte mPendingMessage[3];
|
||||
unsigned mPendingMessageExpectedLength;
|
||||
unsigned mPendingMessageIndex;
|
||||
unsigned mCurrentRpnNumber;
|
||||
unsigned mCurrentNrpnNumber;
|
||||
bool mThruActivated : 1;
|
||||
Thru::Mode mThruFilterMode : 7;
|
||||
MidiMessage mMessage;
|
||||
unsigned long mLastMessageSentTime;
|
||||
unsigned long mLastMessageReceivedTime;
|
||||
unsigned long mSenderActiveSensingPeriodicity;
|
||||
bool mReceiverActiveSensingActivated;
|
||||
int8_t mLastError;
|
||||
|
||||
private:
|
||||
inline StatusByte getStatus(MidiType inType,
|
||||
Channel inChannel) const;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
unsigned encodeSysEx(const byte* inData,
|
||||
byte* outSysEx,
|
||||
unsigned inLength,
|
||||
bool inFlipHeaderBits = false);
|
||||
unsigned decodeSysEx(const byte* inSysEx,
|
||||
byte* outData,
|
||||
unsigned inLength,
|
||||
bool inFlipHeaderBits = false);
|
||||
|
||||
END_MIDI_NAMESPACE
|
||||
|
||||
#include "MIDI.hpp"
|
||||
1485
libraries/FastLED/examples/LuminescentGrand/arduino/MIDI.hpp
Normal file
1485
libraries/FastLED/examples/LuminescentGrand/arduino/MIDI.hpp
Normal file
File diff suppressed because it is too large
Load diff
157
libraries/FastLED/examples/LuminescentGrand/arduino/buttons.h
Normal file
157
libraries/FastLED/examples/LuminescentGrand/arduino/buttons.h
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "fl/ui.h"
|
||||
#include "fl/dbg.h"
|
||||
|
||||
using namespace fl;
|
||||
|
||||
// Done by hand. Old school.
|
||||
class ToggleButton {
|
||||
public:
|
||||
ToggleButton(int pin) : pin_(pin), on_(false), debounce_timestamp_(0), changed_(false) {
|
||||
pinMode(pin_, OUTPUT);
|
||||
digitalWrite(pin_, LOW);
|
||||
delay(1);
|
||||
}
|
||||
|
||||
// true - button is pressed.
|
||||
bool Read() {
|
||||
Update(millis());
|
||||
return changed_;
|
||||
}
|
||||
|
||||
void Update(uint32_t time_now) {
|
||||
if ((time_now - debounce_timestamp_) < 150) {
|
||||
changed_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
int val = Read_Internal();
|
||||
changed_ = on_ != val;
|
||||
|
||||
if (changed_) { // Has the toggle switch changed?
|
||||
on_ = val; // Set the new toggle switch value.
|
||||
// Protect against debouncing artifacts by putting a 200ms delay from the
|
||||
// last time the value changed.
|
||||
if ((time_now - debounce_timestamp_) > 150) {
|
||||
//++curr_val_; // ... and increment the value.
|
||||
debounce_timestamp_ = time_now;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool Read_Internal() {
|
||||
// Toggle the pin back to INPUT and take a reading.
|
||||
pinMode(pin_, INPUT);
|
||||
bool on = (digitalRead(pin_) == HIGH);
|
||||
// Switch the pin back to output so that we can enable the
|
||||
// pulldown resister.
|
||||
pinMode(pin_, OUTPUT);
|
||||
digitalWrite(pin_, LOW);
|
||||
return on;
|
||||
}
|
||||
|
||||
|
||||
int pin_;
|
||||
bool on_;
|
||||
uint32_t debounce_timestamp_;
|
||||
bool changed_;
|
||||
};
|
||||
|
||||
// This is the new type that is built into the midi shield.
|
||||
class MidiShieldButton {
|
||||
public:
|
||||
MidiShieldButton(int pin) : pin_(pin) {
|
||||
pinMode(pin_, INPUT_PULLUP);
|
||||
delay(1);
|
||||
}
|
||||
|
||||
bool Read() {
|
||||
// Toggle the pin back to INPUT and take a reading.
|
||||
int val = digitalRead(pin_) == LOW;
|
||||
|
||||
return val;
|
||||
}
|
||||
private:
|
||||
int pin_;
|
||||
};
|
||||
|
||||
class Potentiometer {
|
||||
public:
|
||||
Potentiometer(int sensor_pin) : sensor_pin_(sensor_pin) {}
|
||||
float Read() {
|
||||
float avg = 0.0;
|
||||
// Filter by reading the value multiple times and taking
|
||||
// the average.
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
avg += analogRead(sensor_pin_);
|
||||
}
|
||||
avg = avg / 8.0f;
|
||||
return avg;
|
||||
}
|
||||
private:
|
||||
int sensor_pin_;
|
||||
};
|
||||
|
||||
typedef MidiShieldButton DigitalButton;
|
||||
|
||||
|
||||
class CountingButton {
|
||||
public:
|
||||
explicit CountingButton(int but_pin) : button_(but_pin), curr_val_(0), mButton("Counting UIButton") {
|
||||
debounce_timestamp_ = millis();
|
||||
on_ = Read();
|
||||
}
|
||||
|
||||
void Update(uint32_t time_now) {
|
||||
bool clicked = mButton.clicked();
|
||||
bool val = Read() || mButton.clicked();
|
||||
bool changed = val != on_;
|
||||
|
||||
if (clicked) {
|
||||
++curr_val_;
|
||||
debounce_timestamp_ = time_now;
|
||||
return;
|
||||
}
|
||||
|
||||
if (changed) { // Has the toggle switch changed?
|
||||
on_ = val; // Set the new toggle switch value.
|
||||
// Protect against debouncing artifacts by putting a 200ms delay from the
|
||||
// last time the value changed.
|
||||
if ((time_now - debounce_timestamp_) > 16) {
|
||||
if (on_) {
|
||||
++curr_val_; // ... and increment the value.
|
||||
}
|
||||
debounce_timestamp_ = time_now;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int curr_val() const { return curr_val_; }
|
||||
|
||||
private:
|
||||
bool Read() {
|
||||
return button_.Read();
|
||||
}
|
||||
|
||||
DigitalButton button_;
|
||||
bool on_;
|
||||
int curr_val_;
|
||||
unsigned long debounce_timestamp_;
|
||||
UIButton mButton;
|
||||
};
|
||||
|
||||
class ColorSelector {
|
||||
public:
|
||||
ColorSelector(int sensor_pin) : but_(sensor_pin) {}
|
||||
|
||||
void Update() {
|
||||
but_.Update(millis());
|
||||
}
|
||||
|
||||
int curr_val() const { return but_.curr_val() % 7; }
|
||||
private:
|
||||
CountingButton but_;
|
||||
};
|
||||
232
libraries/FastLED/examples/LuminescentGrand/arduino/midi_Defs.h
Normal file
232
libraries/FastLED/examples/LuminescentGrand/arduino/midi_Defs.h
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
/*!
|
||||
* @file midi_Defs.h
|
||||
* Project Arduino MIDI Library
|
||||
* @brief MIDI Library for the Arduino - Definitions
|
||||
* @author Francois Best, lathoub
|
||||
* @date 24/02/11
|
||||
* @license MIT - Copyright (c) 2015 Francois Best
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "midi_Namespace.h"
|
||||
|
||||
#if ARDUINO
|
||||
#include <Arduino.h>
|
||||
#else
|
||||
#include <inttypes.h>
|
||||
typedef uint8_t byte;
|
||||
#endif
|
||||
|
||||
BEGIN_MIDI_NAMESPACE
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#define MIDI_CHANNEL_OMNI 0
|
||||
#define MIDI_CHANNEL_OFF 17 // and over
|
||||
|
||||
#define MIDI_PITCHBEND_MIN -8192
|
||||
#define MIDI_PITCHBEND_MAX 8191
|
||||
|
||||
/*! Receiving Active Sensing
|
||||
*/
|
||||
static const uint16_t ActiveSensingTimeout = 300;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Type definitions
|
||||
|
||||
typedef byte StatusByte;
|
||||
typedef byte DataByte;
|
||||
typedef byte Channel;
|
||||
typedef byte FilterMode;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Errors
|
||||
static const uint8_t ErrorParse = 0;
|
||||
static const uint8_t ErrorActiveSensingTimeout = 1;
|
||||
static const uint8_t WarningSplitSysEx = 2;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Aliasing
|
||||
|
||||
using ErrorCallback = void (*)(int8_t);
|
||||
using NoteOffCallback = void (*)(Channel channel, byte note, byte velocity);
|
||||
using NoteOnCallback = void (*)(Channel channel, byte note, byte velocity);
|
||||
using AfterTouchPolyCallback = void (*)(Channel channel, byte note, byte velocity);
|
||||
using ControlChangeCallback = void (*)(Channel channel, byte, byte);
|
||||
using ProgramChangeCallback = void (*)(Channel channel, byte);
|
||||
using AfterTouchChannelCallback = void (*)(Channel channel, byte);
|
||||
using PitchBendCallback = void (*)(Channel channel, int);
|
||||
using SystemExclusiveCallback = void (*)(byte * array, unsigned size);
|
||||
using TimeCodeQuarterFrameCallback = void (*)(byte data);
|
||||
using SongPositionCallback = void (*)(unsigned beats);
|
||||
using SongSelectCallback = void (*)(byte songnumber);
|
||||
using TuneRequestCallback = void (*)(void);
|
||||
using ClockCallback = void (*)(void);
|
||||
using StartCallback = void (*)(void);
|
||||
using TickCallback = void (*)(void);
|
||||
using ContinueCallback = void (*)(void);
|
||||
using StopCallback = void (*)(void);
|
||||
using ActiveSensingCallback = void (*)(void);
|
||||
using SystemResetCallback = void (*)(void);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/*! Enumeration of MIDI types */
|
||||
enum MidiType: uint8_t
|
||||
{
|
||||
InvalidType = 0x00, ///< For notifying errors
|
||||
NoteOff = 0x80, ///< Channel Message - Note Off
|
||||
NoteOn = 0x90, ///< Channel Message - Note On
|
||||
AfterTouchPoly = 0xA0, ///< Channel Message - Polyphonic AfterTouch
|
||||
ControlChange = 0xB0, ///< Channel Message - Control Change / Channel Mode
|
||||
ProgramChange = 0xC0, ///< Channel Message - Program Change
|
||||
AfterTouchChannel = 0xD0, ///< Channel Message - Channel (monophonic) AfterTouch
|
||||
PitchBend = 0xE0, ///< Channel Message - Pitch Bend
|
||||
SystemExclusive = 0xF0, ///< System Exclusive
|
||||
SystemExclusiveStart = SystemExclusive, ///< System Exclusive Start
|
||||
TimeCodeQuarterFrame = 0xF1, ///< System Common - MIDI Time Code Quarter Frame
|
||||
SongPosition = 0xF2, ///< System Common - Song Position Pointer
|
||||
SongSelect = 0xF3, ///< System Common - Song Select
|
||||
Undefined_F4 = 0xF4,
|
||||
Undefined_F5 = 0xF5,
|
||||
TuneRequest = 0xF6, ///< System Common - Tune Request
|
||||
SystemExclusiveEnd = 0xF7, ///< System Exclusive End
|
||||
Clock = 0xF8, ///< System Real Time - Timing Clock
|
||||
Undefined_F9 = 0xF9,
|
||||
Tick = Undefined_F9, ///< System Real Time - Timing Tick (1 tick = 10 milliseconds)
|
||||
Start = 0xFA, ///< System Real Time - Start
|
||||
Continue = 0xFB, ///< System Real Time - Continue
|
||||
Stop = 0xFC, ///< System Real Time - Stop
|
||||
Undefined_FD = 0xFD,
|
||||
ActiveSensing = 0xFE, ///< System Real Time - Active Sensing
|
||||
SystemReset = 0xFF, ///< System Real Time - System Reset
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/*! Enumeration of Thru filter modes */
|
||||
struct Thru
|
||||
{
|
||||
enum Mode
|
||||
{
|
||||
Off = 0, ///< Thru disabled (nothing passes through).
|
||||
Full = 1, ///< Fully enabled Thru (every incoming message is sent back).
|
||||
SameChannel = 2, ///< Only the messages on the Input Channel will be sent back.
|
||||
DifferentChannel = 3, ///< All the messages but the ones on the Input Channel will be sent back.
|
||||
};
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/*! \brief Enumeration of Control Change command numbers.
|
||||
See the detailed controllers numbers & description here:
|
||||
http://www.somascape.org/midi/tech/spec.html#ctrlnums
|
||||
*/
|
||||
enum MidiControlChangeNumber: uint8_t
|
||||
{
|
||||
// High resolution Continuous Controllers MSB (+32 for LSB) ----------------
|
||||
BankSelect = 0,
|
||||
ModulationWheel = 1,
|
||||
BreathController = 2,
|
||||
// CC3 undefined
|
||||
FootController = 4,
|
||||
PortamentoTime = 5,
|
||||
DataEntryMSB = 6,
|
||||
ChannelVolume = 7,
|
||||
Balance = 8,
|
||||
// CC9 undefined
|
||||
Pan = 10,
|
||||
ExpressionController = 11,
|
||||
EffectControl1 = 12,
|
||||
EffectControl2 = 13,
|
||||
// CC14 undefined
|
||||
// CC15 undefined
|
||||
GeneralPurposeController1 = 16,
|
||||
GeneralPurposeController2 = 17,
|
||||
GeneralPurposeController3 = 18,
|
||||
GeneralPurposeController4 = 19,
|
||||
|
||||
DataEntryLSB = 38,
|
||||
|
||||
// Switches ----------------------------------------------------------------
|
||||
Sustain = 64,
|
||||
Portamento = 65,
|
||||
Sostenuto = 66,
|
||||
SoftPedal = 67,
|
||||
Legato = 68,
|
||||
Hold = 69,
|
||||
|
||||
// Low resolution continuous controllers -----------------------------------
|
||||
SoundController1 = 70, ///< Synth: Sound Variation FX: Exciter On/Off
|
||||
SoundController2 = 71, ///< Synth: Harmonic Content FX: Compressor On/Off
|
||||
SoundController3 = 72, ///< Synth: Release Time FX: Distortion On/Off
|
||||
SoundController4 = 73, ///< Synth: Attack Time FX: EQ On/Off
|
||||
SoundController5 = 74, ///< Synth: Brightness FX: Expander On/Off
|
||||
SoundController6 = 75, ///< Synth: Decay Time FX: Reverb On/Off
|
||||
SoundController7 = 76, ///< Synth: Vibrato Rate FX: Delay On/Off
|
||||
SoundController8 = 77, ///< Synth: Vibrato Depth FX: Pitch Transpose On/Off
|
||||
SoundController9 = 78, ///< Synth: Vibrato Delay FX: Flange/Chorus On/Off
|
||||
SoundController10 = 79, ///< Synth: Undefined FX: Special Effects On/Off
|
||||
GeneralPurposeController5 = 80,
|
||||
GeneralPurposeController6 = 81,
|
||||
GeneralPurposeController7 = 82,
|
||||
GeneralPurposeController8 = 83,
|
||||
PortamentoControl = 84,
|
||||
// CC85 to CC90 undefined
|
||||
Effects1 = 91, ///< Reverb send level
|
||||
Effects2 = 92, ///< Tremolo depth
|
||||
Effects3 = 93, ///< Chorus send level
|
||||
Effects4 = 94, ///< Celeste depth
|
||||
Effects5 = 95, ///< Phaser depth
|
||||
DataIncrement = 96,
|
||||
DataDecrement = 97,
|
||||
NRPNLSB = 98, ///< Non-Registered Parameter Number (LSB)
|
||||
NRPNMSB = 99, ///< Non-Registered Parameter Number (MSB)
|
||||
RPNLSB = 100, ///< Registered Parameter Number (LSB)
|
||||
RPNMSB = 101, ///< Registered Parameter Number (MSB)
|
||||
|
||||
// Channel Mode messages ---------------------------------------------------
|
||||
AllSoundOff = 120,
|
||||
ResetAllControllers = 121,
|
||||
LocalControl = 122,
|
||||
AllNotesOff = 123,
|
||||
OmniModeOff = 124,
|
||||
OmniModeOn = 125,
|
||||
MonoModeOn = 126,
|
||||
PolyModeOn = 127
|
||||
};
|
||||
|
||||
struct RPN
|
||||
{
|
||||
enum RegisteredParameterNumbers: uint16_t
|
||||
{
|
||||
PitchBendSensitivity = 0x0000,
|
||||
ChannelFineTuning = 0x0001,
|
||||
ChannelCoarseTuning = 0x0002,
|
||||
SelectTuningProgram = 0x0003,
|
||||
SelectTuningBank = 0x0004,
|
||||
ModulationDepthRange = 0x0005,
|
||||
NullFunction = (0x7f << 7) + 0x7f,
|
||||
};
|
||||
};
|
||||
|
||||
END_MIDI_NAMESPACE
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
/*!
|
||||
* @file midi_Message.h
|
||||
* Project Arduino MIDI Library
|
||||
* @brief MIDI Library for the Arduino - Message struct definition
|
||||
* @author Francois Best
|
||||
* @date 11/06/14
|
||||
* @license MIT - Copyright (c) 2015 Francois Best
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "midi_Namespace.h"
|
||||
#include "midi_Defs.h"
|
||||
#ifndef ARDUINO
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
BEGIN_MIDI_NAMESPACE
|
||||
|
||||
/*! The Message structure contains decoded data of a MIDI message
|
||||
read from the serial port with read()
|
||||
*/
|
||||
template<unsigned SysExMaxSize>
|
||||
struct Message
|
||||
{
|
||||
/*! Default constructor
|
||||
\n Initializes the attributes with their default values.
|
||||
*/
|
||||
inline Message()
|
||||
: channel(0)
|
||||
, type(MIDI_NAMESPACE::InvalidType)
|
||||
, data1(0)
|
||||
, data2(0)
|
||||
, valid(false)
|
||||
{
|
||||
memset(sysexArray, 0, sSysExMaxSize * sizeof(DataByte));
|
||||
}
|
||||
|
||||
/*! The maximum size for the System Exclusive array.
|
||||
*/
|
||||
static const unsigned sSysExMaxSize = SysExMaxSize;
|
||||
|
||||
/*! The MIDI channel on which the message was recieved.
|
||||
\n Value goes from 1 to 16.
|
||||
*/
|
||||
Channel channel;
|
||||
|
||||
/*! The type of the message
|
||||
(see the MidiType enum for types reference)
|
||||
*/
|
||||
MidiType type;
|
||||
|
||||
/*! The first data byte.
|
||||
\n Value goes from 0 to 127.
|
||||
*/
|
||||
DataByte data1;
|
||||
|
||||
/*! The second data byte.
|
||||
If the message is only 2 bytes long, this one is null.
|
||||
\n Value goes from 0 to 127.
|
||||
*/
|
||||
DataByte data2;
|
||||
|
||||
/*! System Exclusive dedicated byte array.
|
||||
\n Array length is stocked on 16 bits,
|
||||
in data1 (LSB) and data2 (MSB)
|
||||
*/
|
||||
DataByte sysexArray[sSysExMaxSize];
|
||||
|
||||
/*! This boolean indicates if the message is valid or not.
|
||||
There is no channel consideration here,
|
||||
validity means the message respects the MIDI norm.
|
||||
*/
|
||||
bool valid;
|
||||
|
||||
/*! Total Length of the message.
|
||||
*/
|
||||
unsigned length;
|
||||
|
||||
inline unsigned getSysExSize() const
|
||||
{
|
||||
const unsigned size = unsigned(data2) << 8 | data1;
|
||||
return size > sSysExMaxSize ? sSysExMaxSize : size;
|
||||
}
|
||||
};
|
||||
|
||||
END_MIDI_NAMESPACE
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*!
|
||||
* @file midi_Namespace.h
|
||||
* Project Arduino MIDI Library
|
||||
* @brief MIDI Library for the Arduino - Namespace declaration
|
||||
* @author Francois Best
|
||||
* @date 24/02/11
|
||||
* @license MIT - Copyright (c) 2015 Francois Best
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define MIDI_NAMESPACE midi
|
||||
#define BEGIN_MIDI_NAMESPACE namespace MIDI_NAMESPACE {
|
||||
#define END_MIDI_NAMESPACE }
|
||||
|
||||
#define USING_NAMESPACE_MIDI using namespace MIDI_NAMESPACE;
|
||||
|
||||
BEGIN_MIDI_NAMESPACE
|
||||
|
||||
END_MIDI_NAMESPACE
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/*!
|
||||
* @file midi_Platform.h
|
||||
* Project Arduino MIDI Library
|
||||
* @brief MIDI Library for the Arduino - Platform
|
||||
* @license MIT - Copyright (c) 2015 Francois Best
|
||||
* @author lathoub
|
||||
* @date 22/03/20
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "midi_Defs.h"
|
||||
|
||||
BEGIN_MIDI_NAMESPACE
|
||||
|
||||
#if ARDUINO
|
||||
|
||||
// DefaultPlatform is the Arduino Platform
|
||||
struct DefaultPlatform
|
||||
{
|
||||
static unsigned long now() { return ::millis(); };
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
struct DefaultPlatform
|
||||
{
|
||||
static unsigned long now() { return 0; };
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
END_MIDI_NAMESPACE
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
/*!
|
||||
* @file midi_Settings.h
|
||||
* Project Arduino MIDI Library
|
||||
* @brief MIDI Library for the Arduino - Settings
|
||||
* @author Francois Best
|
||||
* @date 24/02/11
|
||||
* @license MIT - Copyright (c) 2015 Francois Best
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "midi_Defs.h"
|
||||
|
||||
BEGIN_MIDI_NAMESPACE
|
||||
|
||||
/*! \brief Default Settings for the MIDI Library.
|
||||
|
||||
To change the default settings, don't edit them there, create a subclass and
|
||||
override the values in that subclass, then use the MIDI_CREATE_CUSTOM_INSTANCE
|
||||
macro to create your instance. The settings you don't override will keep their
|
||||
default value. Eg:
|
||||
\code{.cpp}
|
||||
struct MySettings : public midi::DefaultSettings
|
||||
{
|
||||
static const unsigned SysExMaxSize = 1024; // Accept SysEx messages up to 1024 bytes long.
|
||||
};
|
||||
|
||||
MIDI_CREATE_CUSTOM_INSTANCE(HardwareSerial, Serial2, midi, MySettings);
|
||||
\endcode
|
||||
*/
|
||||
struct DefaultSettings
|
||||
{
|
||||
/*! Running status enables short messages when sending multiple values
|
||||
of the same type and channel.\n
|
||||
Must be disabled to send USB MIDI messages to a computer
|
||||
Warning: does not work with some hardware, enable with caution.
|
||||
*/
|
||||
static const bool UseRunningStatus = false;
|
||||
|
||||
/*! NoteOn with 0 velocity should be handled as NoteOf.\n
|
||||
Set to true to get NoteOff events when receiving null-velocity NoteOn messages.\n
|
||||
Set to false to get NoteOn events when receiving null-velocity NoteOn messages.
|
||||
*/
|
||||
static const bool HandleNullVelocityNoteOnAsNoteOff = true;
|
||||
|
||||
/*! Setting this to true will make MIDI.read parse only one byte of data for each
|
||||
call when data is available. This can speed up your application if receiving
|
||||
a lot of traffic, but might induce MIDI Thru and treatment latency.
|
||||
*/
|
||||
static const bool Use1ByteParsing = true;
|
||||
|
||||
/*! Maximum size of SysEx receivable. Decrease to save RAM if you don't expect
|
||||
to receive SysEx, or adjust accordingly.
|
||||
*/
|
||||
static const unsigned SysExMaxSize = 128;
|
||||
|
||||
/*! Global switch to turn on/off sender ActiveSensing
|
||||
Set to true to send ActiveSensing
|
||||
Set to false will not send ActiveSensing message (will also save memory)
|
||||
*/
|
||||
static const bool UseSenderActiveSensing = false;
|
||||
|
||||
/*! Global switch to turn on/off receiver ActiveSensing
|
||||
Set to true to check for message timeouts (via ErrorCallback)
|
||||
Set to false will not check if chained device are still alive (if they use ActiveSensing) (will also save memory)
|
||||
*/
|
||||
static const bool UseReceiverActiveSensing = false;
|
||||
|
||||
/*! Active Sensing is intended to be sent
|
||||
repeatedly by the sender to tell the receiver that a connection is alive. Use
|
||||
of this message is optional. When initially received, the
|
||||
receiver will expect to receive another Active Sensing
|
||||
message each 300ms (max), and if it does not then it will
|
||||
assume that the connection has been terminated. At
|
||||
termination, the receiver will turn off all voices and return to
|
||||
normal (non- active sensing) operation.
|
||||
|
||||
Typical value is 250 (ms) - an Active Sensing command is send every 250ms.
|
||||
(All Roland devices send Active Sensing every 250ms)
|
||||
|
||||
Setting this field to 0 will disable sending MIDI active sensing.
|
||||
*/
|
||||
static const uint16_t SenderActiveSensingPeriodicity = 0;
|
||||
};
|
||||
|
||||
END_MIDI_NAMESPACE
|
||||
125
libraries/FastLED/examples/LuminescentGrand/arduino/serialMIDI.h
Normal file
125
libraries/FastLED/examples/LuminescentGrand/arduino/serialMIDI.h
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
/*!
|
||||
* @file serialMIDI.h
|
||||
* Project Arduino MIDI Library
|
||||
* @brief MIDI Library for the Arduino - Platform
|
||||
* @license MIT - Copyright (c) 2015 Francois Best
|
||||
* @author lathoub, Francois Best
|
||||
* @date 22/03/20
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "midi_Namespace.h"
|
||||
|
||||
BEGIN_MIDI_NAMESPACE
|
||||
|
||||
struct DefaultSerialSettings
|
||||
{
|
||||
/*! Override the default MIDI baudrate to transmit over USB serial, to
|
||||
a decoding program such as Hairless MIDI (set baudrate to 115200)\n
|
||||
http://projectgus.github.io/hairless-midiserial/
|
||||
*/
|
||||
static const long BaudRate = 31250;
|
||||
};
|
||||
|
||||
template <class SerialPort, class _Settings = DefaultSerialSettings>
|
||||
class SerialMIDI
|
||||
{
|
||||
typedef _Settings Settings;
|
||||
|
||||
public:
|
||||
SerialMIDI(SerialPort& inSerial)
|
||||
: mSerial(inSerial)
|
||||
{
|
||||
};
|
||||
|
||||
public:
|
||||
static const bool thruActivated = true;
|
||||
|
||||
void begin()
|
||||
{
|
||||
// Initialise the Serial port
|
||||
#if defined(AVR_CAKE)
|
||||
mSerial. template open<Settings::BaudRate>();
|
||||
#else
|
||||
mSerial.begin(Settings::BaudRate);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool beginTransmission(MidiType)
|
||||
{
|
||||
return true;
|
||||
};
|
||||
|
||||
void write(byte value)
|
||||
{
|
||||
mSerial.write(value);
|
||||
};
|
||||
|
||||
void endTransmission()
|
||||
{
|
||||
};
|
||||
|
||||
byte read()
|
||||
{
|
||||
return mSerial.read();
|
||||
};
|
||||
|
||||
unsigned available()
|
||||
{
|
||||
return mSerial.available();
|
||||
};
|
||||
|
||||
private:
|
||||
SerialPort& mSerial;
|
||||
};
|
||||
|
||||
/*! \brief Create an instance of the library attached to a serial port.
|
||||
You can use HardwareSerial or SoftwareSerial for the serial port.
|
||||
Example: MIDI_CREATE_INSTANCE(HardwareSerial, Serial2, midi2);
|
||||
Then call midi2.begin(), midi2.read() etc..
|
||||
*/
|
||||
#define MIDI_CREATE_INSTANCE(Type, SerialPort, Name) \
|
||||
MIDI_NAMESPACE::SerialMIDI<Type> serial##Name(SerialPort);\
|
||||
MIDI_NAMESPACE::MidiInterface<MIDI_NAMESPACE::SerialMIDI<Type>> Name((MIDI_NAMESPACE::SerialMIDI<Type>&)serial##Name);
|
||||
|
||||
#if defined(ARDUINO_SAM_DUE) || defined(USBCON) || defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__)
|
||||
// Leonardo, Due and other USB boards use Serial1 by default.
|
||||
#define MIDI_CREATE_DEFAULT_INSTANCE() \
|
||||
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI);
|
||||
#else
|
||||
/*! \brief Create an instance of the library with default name, serial port
|
||||
and settings, for compatibility with sketches written with pre-v4.2 MIDI Lib,
|
||||
or if you don't bother using custom names, serial port or settings.
|
||||
*/
|
||||
#define MIDI_CREATE_DEFAULT_INSTANCE() \
|
||||
MIDI_CREATE_INSTANCE(HardwareSerial, Serial, MIDI);
|
||||
#endif
|
||||
|
||||
/*! \brief Create an instance of the library attached to a serial port with
|
||||
custom settings.
|
||||
@see DefaultSettings
|
||||
@see MIDI_CREATE_INSTANCE
|
||||
*/
|
||||
#define MIDI_CREATE_CUSTOM_INSTANCE(Type, SerialPort, Name, Settings) \
|
||||
MIDI_NAMESPACE::SerialMIDI<Type, Settings> serial##Name(SerialPort);\
|
||||
MIDI_NAMESPACE::MidiInterface<MIDI_NAMESPACE::SerialMIDI<Type, Settings>> Name((MIDI_NAMESPACE::SerialMIDI<Type, Settings>&)serial##Name);
|
||||
|
||||
END_MIDI_NAMESPACE
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
|
||||
|
||||
#include "../shared/defs.h"
|
||||
|
||||
|
||||
#if ENABLE_SKETCH
|
||||
|
||||
|
||||
#include "./ui_state.h"
|
||||
#include "shared/Painter.h"
|
||||
#include "fl/dbg.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
//#define UI_V1 // Based off the old midi shield with hand assmebled buttons.
|
||||
#define UI_V2 // Based on a new midi shield with buttons. https://learn.sparkfun.com/tutorials/midi-shield-hookup-guide
|
||||
#define UI_DBG
|
||||
|
||||
#ifdef __STM32F1__
|
||||
// Missing A-type pins, just use digital pins mapped to analog.
|
||||
#define PIN_POT_COLOR_SENSOR D3
|
||||
#define PIN_POT_VEL_SENSOR D4
|
||||
#else
|
||||
#define PIN_POT_COLOR_SENSOR A3
|
||||
#define PIN_POT_VEL_SENSOR A4
|
||||
#endif
|
||||
|
||||
#define PIN_VIS_SELECT 2
|
||||
#define PIN_COLOR_SELECT 4
|
||||
|
||||
Potentiometer velocity_pot(PIN_POT_VEL_SENSOR);
|
||||
Potentiometer color_pot(PIN_POT_COLOR_SENSOR);
|
||||
|
||||
float read_color_selector() {
|
||||
return color_pot.Read();
|
||||
}
|
||||
|
||||
float read_velocity_bias() {
|
||||
return velocity_pot.Read();
|
||||
}
|
||||
|
||||
|
||||
//DigitalButton custom_notecolor_select(4);
|
||||
ColorSelector color_selector(PIN_COLOR_SELECT);
|
||||
CountingButton vis_selector(PIN_VIS_SELECT);
|
||||
|
||||
void ui_init() {
|
||||
}
|
||||
|
||||
|
||||
ui_state ui_update(uint32_t now_ms, uint32_t delta_ms) {
|
||||
ui_state out;
|
||||
vis_selector.Update(now_ms);
|
||||
color_selector.Update();
|
||||
int32_t curr_val = vis_selector.curr_val();
|
||||
FASTLED_DBG("curr_val: " << curr_val);
|
||||
|
||||
out.color_scheme = color_selector.curr_val();
|
||||
|
||||
//bool notecolor_on = custom_notecolor_select.Read();
|
||||
|
||||
//Serial.print("color selector: "); Serial.println(out.color_scheme);
|
||||
|
||||
//float velocity = read_velocity_bias();
|
||||
//float color_selector = read_color_selector();
|
||||
|
||||
//Serial.print("velocity: "); Serial.print(velocity); Serial.print(", color_selector: "); Serial.print(color_selector);
|
||||
|
||||
//if (notecolor_on) {
|
||||
// Serial.print(", notecolor_on: "); Serial.print(notecolor_on);
|
||||
//}
|
||||
|
||||
//Serial.print("color_scheme: "); Serial.print(out.color_scheme); Serial.print(", vis selector: "); Serial.print(curr_val);
|
||||
|
||||
//Serial.println("");
|
||||
|
||||
//Serial.print("curr_val: "); Serial.print(curr_val);// Serial.print(", button state: "); Serial.println(vis_selector.button_.curr_val());;
|
||||
|
||||
out.which_visualizer = static_cast<Painter::VisState>(curr_val % Painter::kNumVisStates);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
#endif // ENABLE_SKETCH
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "buttons.h"
|
||||
|
||||
|
||||
extern ColorSelector color_selector;
|
||||
extern CountingButton vis_selector;
|
||||
|
||||
struct ui_state {
|
||||
ui_state() { memset(this, 0, sizeof(*this)); }
|
||||
int which_visualizer;
|
||||
int color_scheme;
|
||||
//float color_wheel_pos;
|
||||
//float velocity_bias_pos; // default to 1.0f for buttons missing velocity.
|
||||
};
|
||||
|
||||
void ui_init();
|
||||
ui_state ui_update(uint32_t now_ms, uint32_t delta_ms);
|
||||
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
// Copyleft (c) 2012, Zach Vorhies
|
||||
// Public domain, no rights reserved.
|
||||
|
||||
#ifndef APPROXIMATING_FUNCTION_H_
|
||||
#define APPROXIMATING_FUNCTION_H_
|
||||
|
||||
//#include <Arduino.h>
|
||||
|
||||
template<typename X, typename Y>
|
||||
const Y MapT(const X& x,
|
||||
const X& x1, const X& x2,
|
||||
const Y& y1, const Y& y2) {
|
||||
Y return_val = static_cast<Y>((x - x1) * (y2 - y1) / (x2 - x1) + y1);
|
||||
return return_val;
|
||||
}
|
||||
|
||||
template <typename KeyT, typename ValT>
|
||||
struct InterpData {
|
||||
InterpData(const KeyT& k, const ValT& v) : key(k), val(v) {}
|
||||
KeyT key;
|
||||
ValT val;
|
||||
};
|
||||
|
||||
template <typename KeyT, typename ValT>
|
||||
inline void SelectInterpPoints(const KeyT& k,
|
||||
const InterpData<KeyT, ValT>* array,
|
||||
const int n, // Number of elements in array.
|
||||
int* dest_lower_bound,
|
||||
int* dest_upper_bound) {
|
||||
if (n < 1) {
|
||||
*dest_lower_bound = *dest_upper_bound = -1;
|
||||
return;
|
||||
}
|
||||
if (k < array[0].key) {
|
||||
*dest_lower_bound = *dest_upper_bound = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < n - 1; ++i) {
|
||||
const InterpData<KeyT, ValT>& curr = array[i];
|
||||
const InterpData<KeyT, ValT>& next = array[i+1];
|
||||
|
||||
if (curr.key <= k && k <= next.key) {
|
||||
*dest_lower_bound = i;
|
||||
*dest_upper_bound = i+1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
*dest_lower_bound = n - 1;
|
||||
*dest_upper_bound = n - 1;
|
||||
}
|
||||
|
||||
template <typename KeyT, typename ValT>
|
||||
inline ValT Interp(const KeyT& k, const InterpData<KeyT, ValT>* array, const int n) {
|
||||
if (n < 1) {
|
||||
return ValT(0);
|
||||
}
|
||||
|
||||
int low_idx = -1;
|
||||
int high_idx = -1;
|
||||
|
||||
SelectInterpPoints<KeyT, ValT>(k, array, n, &low_idx, &high_idx);
|
||||
|
||||
if (low_idx == high_idx) {
|
||||
return array[low_idx].val;
|
||||
}
|
||||
|
||||
const InterpData<KeyT, ValT>* curr = &array[low_idx];
|
||||
const InterpData<KeyT, ValT>* next = &array[high_idx];
|
||||
// map(...) only works on integers. MapT<> is the same thing but it works on
|
||||
// all datatypes.
|
||||
return MapT<KeyT, ValT>(k, curr->key, next->key, curr->val, next->val);
|
||||
}
|
||||
|
||||
#endif // APPROXIMATING_FUNCTION_H_
|
||||
213
libraries/FastLED/examples/LuminescentGrand/shared/Keyboard.cpp
Normal file
213
libraries/FastLED/examples/LuminescentGrand/shared/Keyboard.cpp
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
|
||||
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "./util.h"
|
||||
#include "./color_mapper.h"
|
||||
#include "./Keyboard.h"
|
||||
#include "./dprint.h"
|
||||
|
||||
Key::Key() : on_(false), sustained_(false), sustain_pedal_on_(false),
|
||||
velocity_(0), idx_(0), event_time_(0) {}
|
||||
|
||||
void Key::SetOn(uint8_t vel, const ColorHSV& color, uint32_t now_ms) {
|
||||
if (curr_color_.v_ < color.v_) { // if the new color is "brighter" than the current color.
|
||||
velocity_ = vel;
|
||||
curr_color_ = color;
|
||||
event_time_ = now_ms;
|
||||
}
|
||||
orig_color_ = curr_color_;
|
||||
event_time_ = now_ms;
|
||||
on_ = true;
|
||||
}
|
||||
|
||||
void Key::SetOff(uint32_t now_ms) {
|
||||
orig_color_ = curr_color_;
|
||||
on_ = false;
|
||||
event_time_ = now_ms;
|
||||
sustained_ = false;
|
||||
}
|
||||
|
||||
void Key::SetSustained() {
|
||||
sustained_ = true;
|
||||
}
|
||||
|
||||
void Key::Update(uint32_t now_ms, uint32_t delta_ms, bool sustain_pedal_on) {
|
||||
if (sustained_ && !sustain_pedal_on) {
|
||||
sustained_ = false;
|
||||
SetOff(now_ms);
|
||||
}
|
||||
sustain_pedal_on_ = sustain_pedal_on;
|
||||
UpdateIntensity(now_ms, delta_ms);
|
||||
}
|
||||
|
||||
float Key::VelocityFactor() const { return velocity_ / 127.f; }
|
||||
|
||||
float Key::CalcAttackDecayFactor(uint32_t delta_ms) const {
|
||||
bool dampened_key = (idx_ < kFirstNoteNoDamp);
|
||||
float active_lights_factor = ::CalcDecayFactor(
|
||||
sustain_pedal_on_,
|
||||
on_,
|
||||
idx_,
|
||||
VelocityFactor(),
|
||||
dampened_key,
|
||||
delta_ms);
|
||||
return active_lights_factor;
|
||||
}
|
||||
|
||||
float Key::AttackRemapFactor(uint32_t now_ms) {
|
||||
if (on_) {
|
||||
return ::AttackRemapFactor(now_ms - event_time_);
|
||||
} else {
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
float Key::IntensityFactor() const {
|
||||
return intensity_;
|
||||
}
|
||||
|
||||
void Key::UpdateIntensity(uint32_t now_ms, uint32_t delta_ms) {
|
||||
if (on_) {
|
||||
// Intensity can be calculated by a
|
||||
float intensity =
|
||||
CalcAttackDecayFactor(now_ms - event_time_) *
|
||||
VelocityFactor() *
|
||||
AttackRemapFactor(now_ms);
|
||||
|
||||
// This is FRAME RATE DEPENDENT FUNCTION!!!!
|
||||
// CHANGE TO TIME INDEPENDENT BEFORE SUBMIT.
|
||||
intensity_ = (.9f * intensity) + (.1f * intensity_);
|
||||
} else if(intensity_ > 0.0f) { // major cpu hotspot.
|
||||
|
||||
if (sustain_pedal_on_) {
|
||||
float delta_s = delta_ms / 1000.f;
|
||||
if (intensity_ > .5f) {
|
||||
const float kRate = .12f;
|
||||
// Time flexible decay function. Stays accurate
|
||||
// even as the frame rate changes.
|
||||
// Formula: A = Pe^(r*t)
|
||||
intensity_ = intensity_ * exp(-delta_s * kRate);
|
||||
} else {
|
||||
// Quickly fade at the bottom end of the transition.
|
||||
const float kRate = .05f;
|
||||
intensity_ -= delta_s * kRate;
|
||||
}
|
||||
} else {
|
||||
float delta_s = delta_ms / 1000.f;
|
||||
if (intensity_ > .5f) {
|
||||
const float kRate = 12.0f;
|
||||
// Time flexible decay function. Stays accurate
|
||||
// even as the frame rate changes.
|
||||
// Formula: A = Pe^(r*t)
|
||||
intensity_ = intensity_ * exp(-delta_s * kRate);
|
||||
} else {
|
||||
// Quickly fade at the bottom end of the transition.
|
||||
const float kRate = 2.0f;
|
||||
intensity_ -= delta_s * kRate;
|
||||
}
|
||||
|
||||
}
|
||||
intensity_ = constrain(intensity_, 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardState::HandleNoteOn(uint8_t midi_note, uint8_t velocity, int color_selector_value, uint32_t now_ms) {
|
||||
if (0 == velocity) {
|
||||
// Some keyboards signify "NoteOff" with a velocity of zero.
|
||||
HandleNoteOff(midi_note, velocity, now_ms);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_KEYBOARD
|
||||
dprint("HandleNoteOn:");
|
||||
|
||||
dprint("midi_note = ");
|
||||
dprint(midi_note);
|
||||
|
||||
dprint(", velocity = ");
|
||||
dprintln(velocity);
|
||||
#endif
|
||||
|
||||
float brightness = ToBrightness(velocity);
|
||||
|
||||
dprint("brightness: "); dprintln(brightness);
|
||||
|
||||
ColorHSV pixel_color_hsv = SelectColor(midi_note, brightness,
|
||||
color_selector_value);
|
||||
|
||||
// TODO: Give a key access to the Keyboard owner, therefore it could inspect the
|
||||
// sustained variable instead of passing it here.
|
||||
Key* key = GetKey(midi_note);
|
||||
|
||||
dprint("key indx: "); dprintln(key->idx_);
|
||||
|
||||
key->SetOn(velocity, pixel_color_hsv, now_ms);
|
||||
}
|
||||
|
||||
void KeyboardState::HandleNoteOff(uint8_t midi_note, uint8_t /*velocity*/, uint32_t now_ms) {
|
||||
#ifdef DEBUG_KEYBOARD
|
||||
dprint("HandleNoteOff:");
|
||||
|
||||
dprint("midi_note = ");
|
||||
dprint(midi_note);
|
||||
|
||||
dprint(", velocity = ");
|
||||
dprintln(velocity);
|
||||
#endif
|
||||
|
||||
Key* key = GetKey(midi_note);
|
||||
|
||||
if (sustain_pedal_) {
|
||||
key->SetSustained();
|
||||
} else {
|
||||
key->SetOff(now_ms);
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardState::HandleControlChange(uint8_t d1, uint8_t d2) {
|
||||
// Note that d1 and d2 just mean "data-1" and "data-2".
|
||||
// TODO: Find out what d1 and d2 should be called.
|
||||
const bool foot_pedal = (d1 == kMidiFootPedal);
|
||||
|
||||
if (foot_pedal) {
|
||||
// Spec says that if that values 0-63 are OFF, otherwise ON.
|
||||
sustain_pedal_ = (d2 >= 64);
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardState::HandleAfterTouchPoly(uint8_t note, uint8_t pressure) {
|
||||
dprintln("HandleAfterTouchPoly");
|
||||
|
||||
dprint("\tnote = ");
|
||||
dprint(note);
|
||||
|
||||
dprint(", pressure = ");
|
||||
dprintln(pressure);
|
||||
}
|
||||
|
||||
KeyboardState::KeyboardState() : sustain_pedal_(false), keys_() {
|
||||
for (int i = 0; i < kNumKeys; ++i) {
|
||||
keys_[i].idx_ = i;
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardState::Update(uint32_t now_ms, uint32_t delta_ms) {
|
||||
for (int i = 0; i < kNumKeys; ++i) {
|
||||
keys_[i].Update(now_ms, delta_ms, sustain_pedal_);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t KeyboardState::KeyIndex(int midi_pitch) {
|
||||
//return constrain(midi_pitch, 21, 108) - 21;
|
||||
return ::KeyIndex(midi_pitch);
|
||||
}
|
||||
|
||||
Key* KeyboardState::GetKey(int midi_pitch) {
|
||||
uint8_t idx = KeyIndex(midi_pitch);
|
||||
return &keys_[idx];
|
||||
}
|
||||
|
||||
|
||||
|
||||
108
libraries/FastLED/examples/LuminescentGrand/shared/Keyboard.h
Normal file
108
libraries/FastLED/examples/LuminescentGrand/shared/Keyboard.h
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
|
||||
#ifndef KEYBOARD_H_
|
||||
#define KEYBOARD_H_
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "color.h"
|
||||
#include "./util.h"
|
||||
|
||||
class KeyboardState;
|
||||
|
||||
// // NOTE: AS OF NOV-12-2013 we've disable all of the auto-sustained
|
||||
// notes in the high end of the keyboard.
|
||||
enum {
|
||||
// kFirstNoteNoDamp = 69, // First key that has no dampener
|
||||
kFirstNoteNoDamp = 89, // DISABLED - Greater than last key.
|
||||
};
|
||||
|
||||
inline uint8_t KeyIndex(int midi_pitch) {
|
||||
return constrain(midi_pitch, 21, 108) - 21;
|
||||
}
|
||||
|
||||
struct Key {
|
||||
Key();
|
||||
void SetOn(uint8_t vel, const ColorHSV& color, uint32_t now_ms);
|
||||
void SetOff(uint32_t now_ms);
|
||||
void SetSustained();
|
||||
|
||||
void Update(uint32_t now_ms, uint32_t delta_ms, bool sustain_pedal_on);
|
||||
|
||||
float VelocityFactor() const;
|
||||
float CalcAttackDecayFactor(uint32_t delta_ms) const;
|
||||
float AttackRemapFactor(uint32_t now_ms);
|
||||
float IntensityFactor() const;
|
||||
void UpdateIntensity(uint32_t now_ms, uint32_t delta_ms);
|
||||
|
||||
bool on_; // Max number of MIDI keys.
|
||||
bool sustained_;
|
||||
bool sustain_pedal_on_;
|
||||
uint8_t velocity_;
|
||||
int idx_;
|
||||
unsigned long event_time_;
|
||||
|
||||
// 0.0 -> 1.0 How intense the key is, used for light sequences to represent
|
||||
// 0 -> 0% of lights on to 1.0 -> 100% of lights on. this is a smooth
|
||||
// value through time.
|
||||
float intensity_;
|
||||
ColorHSV orig_color_;
|
||||
ColorHSV curr_color_;
|
||||
};
|
||||
|
||||
// Interface into the Keyboard state.
|
||||
// Convenience class which holds all the keys in the keyboard. Also
|
||||
// has a convenience function will allows one to map the midi notes
|
||||
// (21-108) to the midi keys (0-88).
|
||||
class KeyboardState {
|
||||
public:
|
||||
|
||||
// NOTE: AS OF NOV-12-2013 we've disable all of the auto-sustained
|
||||
// notes in the high end of the keyboard.
|
||||
//enum {
|
||||
// kFirstNoteNoDamp = 69, // First key that has no dampener
|
||||
// kFirstNoteNoDamp = 89, // DISABLED - Greater than last key.
|
||||
//};
|
||||
|
||||
KeyboardState();
|
||||
void Update(uint32_t now_ms, uint32_t delta_ms);
|
||||
|
||||
|
||||
////////////////////////////////////
|
||||
// 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(uint8_t midi_note, uint8_t velocity, int color_selector_value, uint32_t now_ms);
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
// 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(uint8_t midi_note, uint8_t velocity, uint32_t now_ms);
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
// 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(uint8_t note, uint8_t pressure);
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
// Detects whether the foot pedal has been touched.
|
||||
void HandleControlChange(uint8_t d1, uint8_t d2);
|
||||
|
||||
|
||||
static uint8_t KeyIndex(int midi_pitch);
|
||||
|
||||
Key* GetKey(int midi_pitch);
|
||||
|
||||
static const int kNumKeys = 88;
|
||||
bool sustain_pedal_;
|
||||
Key keys_[kNumKeys];
|
||||
};
|
||||
|
||||
|
||||
#endif // KEYBOARD_H_
|
||||
|
||||
485
libraries/FastLED/examples/LuminescentGrand/shared/Painter.cpp
Normal file
485
libraries/FastLED/examples/LuminescentGrand/shared/Painter.cpp
Normal file
|
|
@ -0,0 +1,485 @@
|
|||
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "./Painter.h"
|
||||
#include "./led_layout_array.h"
|
||||
#include "./dprint.h"
|
||||
#include "./Keyboard.h"
|
||||
#include "fl/math_macros.h"
|
||||
#include "fl/math.h"
|
||||
#include "fl/warn.h"
|
||||
|
||||
namespace {
|
||||
|
||||
float LuminanceDecay(float time) {
|
||||
typedef InterpData<float, float> Datum;
|
||||
static const Datum kData[] = {
|
||||
Datum(0, 0),
|
||||
Datum(1, 0),
|
||||
Datum(10, 0),
|
||||
Datum(47, 60),
|
||||
Datum(120, 100),
|
||||
Datum(230, 160),
|
||||
Datum(250, 255),
|
||||
Datum(254, 255),
|
||||
Datum(255, 64),
|
||||
};
|
||||
|
||||
const float key = time * 255.f;
|
||||
static const int n = sizeof(kData) / sizeof(kData[0]);
|
||||
float approx_val = Interp(key, kData, n);
|
||||
|
||||
|
||||
static const float k = (1.0f / 255.f);
|
||||
const float out = approx_val * k;
|
||||
return out;
|
||||
}
|
||||
|
||||
float CalcLuminance(float time_delta_ms,
|
||||
bool sustain_pedal_on,
|
||||
const Key& key,
|
||||
int key_idx) {
|
||||
|
||||
if (key.curr_color_.v_ <= 0.0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
const bool dampened_key = (key_idx < kFirstNoteNoDamp);
|
||||
|
||||
const float decay_factor = CalcDecayFactor(sustain_pedal_on,
|
||||
key.on_,
|
||||
key_idx,
|
||||
key.velocity_ * (1.f/127.f), // Normalizing
|
||||
dampened_key,
|
||||
time_delta_ms);
|
||||
|
||||
if (key.on_) {
|
||||
//const float brigthness_factor = sin(key.orig_color_.v_ * PI / 2.0);
|
||||
float brigthness_factor = 0.0f;
|
||||
|
||||
if (kUseLedCurtin) {
|
||||
brigthness_factor = sqrt(sqrt(key.orig_color_.v_));
|
||||
} else {
|
||||
//brigthness_factor = key.orig_color_.v_ * key.orig_color_.v_;
|
||||
brigthness_factor = key.orig_color_.v_;
|
||||
}
|
||||
return LuminanceDecay(decay_factor) * brigthness_factor;
|
||||
//return 1.0f;
|
||||
} else {
|
||||
return decay_factor * key.orig_color_.v_;
|
||||
}
|
||||
}
|
||||
|
||||
float CalcSaturation(float time_delta_ms, const ColorHSV& color, bool key_on) {
|
||||
if (color.v_ <= 0.0) {
|
||||
return color.s_;
|
||||
}
|
||||
if (!key_on) {
|
||||
return 1.0f;
|
||||
}
|
||||
static const float kDefaultSaturationTime = 0.05f * 1000.f;
|
||||
|
||||
// At time = 0.0 the saturation_factor will be at 0.0 and then transition to 1.0
|
||||
float saturation_factor = mapf(time_delta_ms,
|
||||
0.0f, kDefaultSaturationTime,
|
||||
0.0f, 1.0f);
|
||||
// As time increases the saturation factor will continue
|
||||
// to grow past 1.0. We use min to clamp it back to 1.0.
|
||||
saturation_factor = MIN(1.0f, saturation_factor);
|
||||
// TODO - make the saturation interpolate between the original
|
||||
// color and the unsaturated state.
|
||||
return saturation_factor;
|
||||
}
|
||||
|
||||
} // namespace.
|
||||
|
||||
|
||||
void Painter::Paint(uint32_t now_ms,
|
||||
uint32_t delta_ms,
|
||||
VisState vis_state,
|
||||
KeyboardState* keyboard,
|
||||
LedRopeInterface* light_rope) {
|
||||
for (int i = 0; i < KeyboardState::kNumKeys; ++i) {
|
||||
Key& key = keyboard->keys_[i];
|
||||
|
||||
const float time_delta_ms = static_cast<float>(now_ms - key.event_time_);
|
||||
|
||||
const float lum = CalcLuminance(time_delta_ms, keyboard->sustain_pedal_, key, i);
|
||||
const float sat = CalcSaturation(time_delta_ms, key.curr_color_, key.on_);
|
||||
|
||||
//if (key.idx_ == 56) {
|
||||
// dprint("lum: "); dprint(lum*255.f); dprint(" sat:"); dprintln(sat*255.f);
|
||||
//}
|
||||
|
||||
key.curr_color_.v_ = lum;
|
||||
key.curr_color_.s_ = sat;
|
||||
|
||||
// Removing this line breaks one of the visualizers...
|
||||
// TODO: Figure out a cleaner solution.
|
||||
light_rope->Set(i, key.curr_color_.ToRGB());
|
||||
}
|
||||
|
||||
LedColumns led_columns = LedLayoutArray();
|
||||
|
||||
switch (vis_state) {
|
||||
case Painter::kBlockNote: {
|
||||
light_rope->DrawSequentialRepeat(kNumLightsPerNote);
|
||||
break;
|
||||
}
|
||||
case Painter::kColumnNote: {
|
||||
light_rope->DrawRepeat(led_columns.array, kNumKeys);
|
||||
break;
|
||||
}
|
||||
case Painter::kVUNote: {
|
||||
PaintVuNotes(now_ms, *keyboard, led_columns.array, kNumKeys, light_rope);
|
||||
break;
|
||||
}
|
||||
case Painter::kVUMidNote: {
|
||||
PaintVuMidNotesFade(delta_ms, *keyboard, led_columns.array, kNumKeys, light_rope);
|
||||
break;
|
||||
}
|
||||
|
||||
case Painter::kVegas: { // aka "vegas mode?"
|
||||
VegasVisualizer(*keyboard, led_columns.array, kNumKeys, light_rope);
|
||||
break;
|
||||
}
|
||||
|
||||
case Painter::kBrightSurprise: {
|
||||
PaintBrightSurprise(*keyboard, led_columns.array, kNumKeys, light_rope);
|
||||
break;
|
||||
}
|
||||
|
||||
case Painter::kVUSpaceInvaders: {
|
||||
PaintVuSpaceInvaders(now_ms, *keyboard, led_columns.array, kNumKeys, light_rope);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
dprint("Unknown mode: "); dprint(vis_state); dprint(".\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Painter::PaintVuNotes(uint32_t /*now_ms*/,
|
||||
const KeyboardState& keyboard,
|
||||
const int* led_column_table, int led_column_table_length,
|
||||
LedRopeInterface* led_rope) {
|
||||
|
||||
|
||||
FASTLED_WARN("\n\n############## VU NOTES ################\n\n");
|
||||
|
||||
led_rope->RawBeginDraw();
|
||||
|
||||
for (int i = 0; i < led_column_table_length; ++i) {
|
||||
const Key& key = keyboard.keys_[i];
|
||||
|
||||
|
||||
// Map the white keys to the bottom and the black keys to the top.
|
||||
bool black_key = false;
|
||||
switch (key.idx_ % 12) {
|
||||
case 1:
|
||||
case 4:
|
||||
case 6:
|
||||
case 9:
|
||||
case 11:
|
||||
black_key = true;
|
||||
break;
|
||||
}
|
||||
|
||||
const int pixel_count = led_column_table[i];
|
||||
const int draw_pixel_count = ceil(pixel_count * sqrt(key.curr_color_.v_));
|
||||
|
||||
const int black_pixel_count = pixel_count - draw_pixel_count;
|
||||
const Color3i& c = *led_rope->GetIterator(i);
|
||||
|
||||
|
||||
const bool reverse_correct = black_key == (key.idx_ % 2);
|
||||
|
||||
if (reverse_correct) {
|
||||
for (int j = 0; j < draw_pixel_count; ++j) {
|
||||
if (j < draw_pixel_count - 1) {
|
||||
led_rope->RawDrawPixel(c);
|
||||
} else {
|
||||
// Last pixel.
|
||||
ColorHSV hsv(random(512) / 512.f, random(512) / 512.f, 1.0);
|
||||
led_rope->RawDrawPixel(hsv.ToRGB());
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < black_pixel_count; ++j) {
|
||||
led_rope->RawDrawPixel(Color3i::Black());
|
||||
}
|
||||
} else {
|
||||
for (int j = 0; j < black_pixel_count; ++j) {
|
||||
led_rope->RawDrawPixel(Color3i::Black());
|
||||
}
|
||||
|
||||
for (int j = draw_pixel_count - 1; j >= 0; --j) {
|
||||
if (j < draw_pixel_count - 1) {
|
||||
led_rope->RawDrawPixel(c);
|
||||
} else {
|
||||
// Last pixel.
|
||||
ColorHSV hsv(random(512) / 512.f, random(512) / 512.f, 1.0);
|
||||
led_rope->RawDrawPixel(hsv.ToRGB());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
led_rope->RawCommitDraw();
|
||||
}
|
||||
|
||||
void Painter::PaintVuMidNotesFade(uint32_t /*delta_ms*/,
|
||||
const KeyboardState& keyboard,
|
||||
const int* led_column_table, int led_column_table_length,
|
||||
LedRopeInterface* led_rope) {
|
||||
|
||||
FASTLED_WARN("\n\n############## VU MID NOTES FADE ################\n\n");
|
||||
|
||||
struct DrawPoints {
|
||||
int n_black0;
|
||||
int n_fade0;
|
||||
int n_fill;
|
||||
int n_fade1;
|
||||
int n_black1;
|
||||
float fade_factor; // 0->1.0
|
||||
|
||||
float SumBrightness() const {
|
||||
float out = 0;
|
||||
out += n_fill;
|
||||
out += (fade_factor * n_fade0);
|
||||
out += (fade_factor * n_fade1);
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
// Generator for the DrawPoints struct above.
|
||||
// n_led: How many led's there are in total.
|
||||
// factor: 0->1, indicates % of led's "on".
|
||||
struct F {
|
||||
static DrawPoints Generate(int n_led, float factor) {
|
||||
DrawPoints out;
|
||||
memset(&out, 0, sizeof(out));
|
||||
if (n_led == 0 || factor == 0.0f) {
|
||||
out.n_black0 = n_led;
|
||||
return out;
|
||||
}
|
||||
const int is_odd = (n_led % 2);
|
||||
const int n_half_lights = n_led / 2 + is_odd;
|
||||
const float f_half_fill = n_half_lights * factor;
|
||||
const int n_half_fill = static_cast<int>(f_half_fill); // Truncates float.
|
||||
|
||||
float fade_pix_perc = f_half_fill - static_cast<float>(n_half_fill);
|
||||
int n_fade_pix = fade_pix_perc < 1.0f;
|
||||
if (n_half_fill == 0) {
|
||||
n_fade_pix = 1;
|
||||
}
|
||||
int n_half_black = n_half_lights - n_half_fill - n_fade_pix;
|
||||
|
||||
int n_fill_pix = 0;
|
||||
if (n_half_fill > 0) {
|
||||
n_fill_pix = n_half_fill * 2 + (is_odd ? -1 : 0);
|
||||
}
|
||||
|
||||
out.n_black0 = n_half_black;
|
||||
out.n_fade0 = n_fade_pix;
|
||||
out.n_fill = n_fill_pix;
|
||||
out.n_fade1 = n_fade_pix;
|
||||
if (!n_fill_pix && is_odd) {
|
||||
out.n_fade1 = 0;
|
||||
}
|
||||
out.n_black1 = n_half_black;
|
||||
out.fade_factor = fade_pix_perc;
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
led_rope->RawBeginDraw();
|
||||
|
||||
for (int i = 0; i < led_column_table_length; ++i) {
|
||||
const Key& key = keyboard.keys_[i];
|
||||
|
||||
float active_lights_factor = key.IntensityFactor();
|
||||
|
||||
//if (key.curr_color_.v_ <= 0.f) {
|
||||
// active_lights_factor = 0.0;
|
||||
//}
|
||||
|
||||
const int n_led = led_column_table[i];
|
||||
|
||||
if (active_lights_factor > 0.0f) {
|
||||
DrawPoints dp = F::Generate(n_led, active_lights_factor);
|
||||
|
||||
ColorHSV hsv = key.curr_color_;
|
||||
hsv.v_ = 1.0;
|
||||
Color3i color = hsv.ToRGB();
|
||||
// Now figure out optional fade color
|
||||
Color3i fade_col;
|
||||
ColorHSV c = key.curr_color_;
|
||||
c.v_ = dp.fade_factor;
|
||||
fade_col = c.ToRGB();
|
||||
|
||||
// Output to graphics.
|
||||
led_rope->RawDrawPixels(Color3i::Black(), dp.n_black0);
|
||||
led_rope->RawDrawPixels(fade_col, dp.n_fade0);
|
||||
led_rope->RawDrawPixels(color, dp.n_fill);
|
||||
led_rope->RawDrawPixels(fade_col, dp.n_fade1);
|
||||
led_rope->RawDrawPixels(Color3i::Black(), dp.n_black1);
|
||||
|
||||
#ifdef DEBUG_PAINTER
|
||||
if (active_lights_factor > 0.0) {
|
||||
int total_lights_on = dp.SumBrightness();
|
||||
//dprint("total_lights_on: "); dprint(total_lights_on);
|
||||
//dprint(", total lights written: "); dprintln(total_lights_on + dp.n_black0 + dp.n_black1);
|
||||
|
||||
//float total = (dp.n_fade0 * dp.fade_factor) + (dp.n_fade1 * dp.fade_factor) + static_cast<float>(dp.n_fill);
|
||||
#define P(X) dprint(", "#X ": "); dprint(X);
|
||||
|
||||
//dprint("active_lights_factor: "); dprintln(active_lights_factor);
|
||||
|
||||
//P(dp.n_black0); P(dp.n_fade0); P(dp.n_fill); P(dp.n_fade1); P(dp.n_black1); P(dp.fade_factor);
|
||||
P(total_lights_on);
|
||||
P(active_lights_factor);
|
||||
//P(total);
|
||||
dprintln("");
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
led_rope->RawDrawPixels(Color3i::Black(), n_led);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
led_rope->RawCommitDraw();
|
||||
}
|
||||
|
||||
void Painter::VegasVisualizer(const KeyboardState& keyboard,
|
||||
const int* led_column_table, int led_column_table_length,
|
||||
LedRopeInterface* led_rope) {
|
||||
|
||||
led_rope->RawBeginDraw();
|
||||
|
||||
uint32_t skipped_lights = 0;
|
||||
for (int i = 0; i < led_column_table_length; ++i) {
|
||||
const Key& key = keyboard.keys_[i];
|
||||
uint32_t painted_lights = 0;
|
||||
|
||||
// % of lights that are active.
|
||||
const float active_lights_factor = led_column_table[i] * sqrt(key.curr_color_.v_);
|
||||
const float inactive_lights_factor = 1.0f - active_lights_factor;
|
||||
const float taper_point_1 = inactive_lights_factor / 2.0f;
|
||||
const float taper_point_2 = taper_point_1 + active_lights_factor;
|
||||
|
||||
const int taper_idx_1 = static_cast<int>(floor(taper_point_1 * led_column_table[i]));
|
||||
const int taper_idx_2 = static_cast<int>(floor(taper_point_2 * led_column_table[i]));
|
||||
|
||||
const Color3i c = key.curr_color_.ToRGB();
|
||||
|
||||
for (int i = 0; i < taper_idx_1 / 2; ++i) {
|
||||
led_rope->RawDrawPixel(Color3i::Black());
|
||||
painted_lights++;
|
||||
}
|
||||
|
||||
int length = taper_idx_2 - taper_idx_1;
|
||||
for (int i = 0; i < min(200, length); ++i) {
|
||||
led_rope->RawDrawPixel(c);
|
||||
painted_lights++;
|
||||
}
|
||||
|
||||
length = led_column_table[i] - taper_idx_2;
|
||||
for (int i = 0; i < length; ++i) {
|
||||
led_rope->RawDrawPixel(Color3i::Black());
|
||||
painted_lights++;
|
||||
}
|
||||
skipped_lights += MAX(0, static_cast<int32_t>(led_column_table[i]) - static_cast<int32_t>(painted_lights));
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < skipped_lights; ++i) {
|
||||
led_rope->RawDrawPixel(Color3i::Black());
|
||||
}
|
||||
|
||||
led_rope->RawCommitDraw();
|
||||
}
|
||||
|
||||
void Painter::PaintBrightSurprise(
|
||||
const KeyboardState& keyboard,
|
||||
const int* led_column_table, int led_column_table_length,
|
||||
LedRopeInterface* led_rope) {
|
||||
|
||||
led_rope->RawBeginDraw();
|
||||
int total_counted = 0;
|
||||
|
||||
float r, g, b;
|
||||
r = g = b = 0;
|
||||
|
||||
for (int i = 0; i < KeyboardState::kNumKeys; ++i) {
|
||||
const Key& key = keyboard.keys_[i];
|
||||
|
||||
|
||||
if (key.curr_color_.v_ > 0.0f) {
|
||||
const Color3i rgb = key.curr_color_.ToRGB();
|
||||
r += rgb.r_;
|
||||
g += rgb.g_;
|
||||
b += rgb.b_;
|
||||
++total_counted;
|
||||
}
|
||||
}
|
||||
|
||||
float denom = total_counted ? total_counted : 1;
|
||||
r /= denom;
|
||||
g /= denom;
|
||||
b /= denom;
|
||||
|
||||
|
||||
const Color3i rgb(r, g, b);
|
||||
|
||||
for (int i = 0; i < led_column_table_length; ++i) {
|
||||
const int n = led_column_table[i];
|
||||
for (int i = 0; i < n; ++i) {
|
||||
led_rope->RawDrawPixel(rgb);
|
||||
}
|
||||
}
|
||||
led_rope->RawCommitDraw();
|
||||
}
|
||||
|
||||
void Painter::PaintVuSpaceInvaders(uint32_t /*now_ms*/,
|
||||
const KeyboardState& keyboard,
|
||||
const int* led_column_table, int led_column_table_length,
|
||||
LedRopeInterface* led_rope) {
|
||||
led_rope->RawBeginDraw();
|
||||
|
||||
Color3i black = Color3i::Black();
|
||||
|
||||
for (int i = 0; i < led_column_table_length; ++i) {
|
||||
const Key& key = keyboard.keys_[i];
|
||||
|
||||
const int pixel_count = led_column_table[i];
|
||||
const int draw_pixel_count = ceil(pixel_count * sqrt(key.curr_color_.v_));
|
||||
|
||||
const int black_pixel_count = pixel_count - draw_pixel_count;
|
||||
|
||||
// If i is even
|
||||
if (i % 2 == 0) {
|
||||
for (int j = 0; j < black_pixel_count; ++j) {
|
||||
led_rope->RawDrawPixel(*led_rope->GetIterator(i));
|
||||
}
|
||||
for (int j = 0; j < draw_pixel_count; ++j) {
|
||||
led_rope->RawDrawPixel(black);
|
||||
}
|
||||
} else {
|
||||
|
||||
for (int j = 0; j < draw_pixel_count; ++j) {
|
||||
led_rope->RawDrawPixel(black);
|
||||
}
|
||||
|
||||
for (int j = 0; j < black_pixel_count; ++j) {
|
||||
led_rope->RawDrawPixel(*led_rope->GetIterator(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
led_rope->RawCommitDraw();
|
||||
}
|
||||
58
libraries/FastLED/examples/LuminescentGrand/shared/Painter.h
Normal file
58
libraries/FastLED/examples/LuminescentGrand/shared/Painter.h
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
#ifndef PAINTER_H
|
||||
#define PAINTER_H
|
||||
|
||||
#include "./Keyboard.h"
|
||||
#include "./ApproximatingFunction.h"
|
||||
#include "./util.h"
|
||||
#include "./settings.h"
|
||||
#include "./led_rope_interface.h"
|
||||
|
||||
struct Painter {
|
||||
|
||||
enum VisState {
|
||||
kVUMidNote = 0,
|
||||
kColumnNote,
|
||||
kBlockNote,
|
||||
kVUNote,
|
||||
kVUSpaceInvaders,
|
||||
kVegas,
|
||||
kBrightSurprise,
|
||||
|
||||
kNumVisStates,
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
static void Paint(uint32_t now_ms,
|
||||
uint32_t delta_ms,
|
||||
VisState vis_state,
|
||||
KeyboardState* keyboard,
|
||||
LedRopeInterface* light_rope);
|
||||
private:
|
||||
static void PaintVuNotes(uint32_t now_ms,
|
||||
const KeyboardState& keyboard,
|
||||
const int* led_column_table, int led_column_table_length,
|
||||
LedRopeInterface* led_rope);
|
||||
|
||||
static void PaintVuMidNotesFade(uint32_t delta_ms,
|
||||
const KeyboardState& keyboard,
|
||||
const int* led_column_table, int led_column_table_length,
|
||||
LedRopeInterface* led_rope);
|
||||
|
||||
// This is a crazy effect, lets keep this around.
|
||||
static void VegasVisualizer(const KeyboardState& keyboard,
|
||||
const int* led_column_table, int led_column_table_length,
|
||||
LedRopeInterface* led_rope);
|
||||
|
||||
static void PaintBrightSurprise(const KeyboardState& keyboard,
|
||||
const int* led_column_table, int led_column_table_length,
|
||||
LedRopeInterface* led_rope);
|
||||
|
||||
|
||||
|
||||
static void PaintVuSpaceInvaders(uint32_t now_ms,
|
||||
const KeyboardState& keyboard,
|
||||
const int* led_column_table, int led_column_table_length,
|
||||
LedRopeInterface* led_rope);
|
||||
};
|
||||
|
||||
#endif // PAINTER_H
|
||||
151
libraries/FastLED/examples/LuminescentGrand/shared/color.cpp
Normal file
151
libraries/FastLED/examples/LuminescentGrand/shared/color.cpp
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "./color.h"
|
||||
#include "./util.h"
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void Color3i::Mul(const Color3i& other_color) {
|
||||
int r = r_;
|
||||
int g = g_;
|
||||
int b = b_;
|
||||
|
||||
r = r * int(other_color.r_) / 255;
|
||||
g = g * int(other_color.g_) / 255;
|
||||
b = b * int(other_color.b_) / 255;
|
||||
Set(r, g, b);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void Color3i::Mulf(float scale) {
|
||||
const int s = static_cast<int>(scale * 255.0f);
|
||||
|
||||
int r = static_cast<int>(r_) * s / 255;
|
||||
int g = static_cast<int>(g_) * s / 255;
|
||||
int b = static_cast<int>(b_) * s / 255;
|
||||
|
||||
Set(r, g, b);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void Color3i::Sub(const Color3i& color) {
|
||||
if (r_ < color.r_) r_ = 0;
|
||||
else r_ -= color.r_;
|
||||
if (g_ < color.g_) g_ = 0;
|
||||
else g_ -= color.g_;
|
||||
if (b_ < color.b_) b_ = 0;
|
||||
else b_ -= color.b_;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void Color3i::Add(const Color3i& color) {
|
||||
if ((255 - r_) < color.r_) r_ = 255;
|
||||
else r_ += color.r_;
|
||||
if ((255 - g_) < color.g_) g_ = 255;
|
||||
else g_ += color.g_;
|
||||
if ((255 - b_) < color.b_) b_ = 255;
|
||||
else b_ += color.b_;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
uint8_t Color3i::Get(int rgb_index) const {
|
||||
const uint8_t* rgb = At(rgb_index);
|
||||
return rgb ? *rgb : 0;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void Color3i::Set(int rgb_index, uint8_t val) {
|
||||
uint8_t* rgb = At(rgb_index);
|
||||
if (rgb) {
|
||||
*rgb = val;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void Color3i::Interpolate(const Color3i& other_color, float t) {
|
||||
if (0.0f >= t) {
|
||||
Set(other_color);
|
||||
} else if (1.0f <= t) {
|
||||
return;
|
||||
}
|
||||
|
||||
Color3i new_color = other_color;
|
||||
new_color.Mul(1.0f - t);
|
||||
this->Mul(t);
|
||||
this->Add(new_color);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
uint8_t* Color3i::At(int rgb_index) {
|
||||
switch(rgb_index) {
|
||||
case 0: return &r_;
|
||||
case 1: return &g_;
|
||||
case 2: return &b_;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
const uint8_t* Color3i::At(int rgb_index) const {
|
||||
switch(rgb_index) {
|
||||
case 0: return &r_;
|
||||
case 1: return &g_;
|
||||
case 2: return &b_;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void ColorHSV::FromRGB(const Color3i& rgb) {
|
||||
typedef double FloatT;
|
||||
FloatT r = (FloatT) rgb.r_/255.f;
|
||||
FloatT g = (FloatT) rgb.g_/255.f;
|
||||
FloatT b = (FloatT) rgb.b_/255.f;
|
||||
FloatT max_rgb = max(r, max(g, b));
|
||||
FloatT min_rgb = min(r, min(g, b));
|
||||
v_ = max_rgb;
|
||||
|
||||
FloatT d = max_rgb - min_rgb;
|
||||
s_ = max_rgb == 0 ? 0 : d / max_rgb;
|
||||
|
||||
if (max_rgb == min_rgb) {
|
||||
h_ = 0; // achromatic
|
||||
} else {
|
||||
if (max_rgb == r) {
|
||||
h_ = (g - b) / d + (g < b ? 6 : 0);
|
||||
} else if (max_rgb == g) {
|
||||
h_ = (b - r) / d + 2;
|
||||
} else if (max_rgb == b) {
|
||||
h_ = (r - g) / d + 4;
|
||||
}
|
||||
h_ /= 6;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
Color3i ColorHSV::ToRGB() const {
|
||||
typedef double FloatT;
|
||||
FloatT r = 0;
|
||||
FloatT g = 0;
|
||||
FloatT b = 0;
|
||||
|
||||
int i = int(h_ * 6);
|
||||
FloatT f = h_ * 6.0 - static_cast<FloatT>(i);
|
||||
FloatT p = v_ * (1.0 - s_);
|
||||
FloatT q = v_ * (1.0 - f * s_);
|
||||
FloatT t = v_ * (1.0 - (1.0 - f) * s_);
|
||||
|
||||
switch(i % 6){
|
||||
case 0: r = v_, g = t, b = p; break;
|
||||
case 1: r = q, g = v_, b = p; break;
|
||||
case 2: r = p, g = v_, b = t; break;
|
||||
case 3: r = p, g = q, b = v_; break;
|
||||
case 4: r = t, g = p, b = v_; break;
|
||||
case 5: r = v_, g = p, b = q; break;
|
||||
}
|
||||
|
||||
return Color3i(round(r * 255), round(g * 255), round(b * 255));
|
||||
}
|
||||
|
||||
102
libraries/FastLED/examples/LuminescentGrand/shared/color.h
Normal file
102
libraries/FastLED/examples/LuminescentGrand/shared/color.h
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
|
||||
|
||||
#ifndef COLOR_H_
|
||||
#define COLOR_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct Color3i {
|
||||
static Color3i Black() { return Color3i(0x0, 0x0, 0x0); }
|
||||
static Color3i White() { return Color3i(0xff, 0xff, 0xff); }
|
||||
static Color3i Red() { return Color3i(0xff, 0x00, 0x00); }
|
||||
static Color3i Orange() { return Color3i(0xff, 0xff / 2,00); }
|
||||
static Color3i Yellow() { return Color3i(0xff, 0xff,00); }
|
||||
static Color3i Green() { return Color3i(0x00, 0xff, 0x00); }
|
||||
static Color3i Cyan() { return Color3i(0x00, 0xff, 0xff); }
|
||||
static Color3i Blue() { return Color3i(0x00, 0x00, 0xff); }
|
||||
Color3i(uint8_t r, uint8_t g, uint8_t b) { Set(r,g,b); }
|
||||
Color3i() { Set(0xff, 0xff, 0xff); }
|
||||
Color3i(const Color3i& other) { Set(other); }
|
||||
|
||||
void Set(uint8_t r, uint8_t g, uint8_t b) { r_ = r; g_ = g; b_ = b; }
|
||||
void Set(const Color3i& c) { Set(c.r_, c.g_, c.b_); }
|
||||
void Mul(const Color3i& other_color);
|
||||
void Mulf(float scale); // Input range is 0.0 -> 1.0
|
||||
void Mul(uint8_t val) {
|
||||
Mul(Color3i(val, val, val));
|
||||
}
|
||||
void Sub(const Color3i& color);
|
||||
void Add(const Color3i& color);
|
||||
uint8_t Get(int rgb_index) const;
|
||||
void Set(int rgb_index, uint8_t val);
|
||||
void Fill(uint8_t val) { Set(val, val, val); }
|
||||
uint8_t MaxRGB() const {
|
||||
uint8_t max_r_g = r_ > g_ ? r_ : g_;
|
||||
return max_r_g > b_ ? max_r_g : b_;
|
||||
}
|
||||
|
||||
template <typename PrintStream>
|
||||
inline void Print(PrintStream* stream) const {
|
||||
stream->print("RGB:\t");
|
||||
stream->print(r_); stream->print(",\t");
|
||||
stream->print(g_); stream->print(",\t");
|
||||
stream->print(b_);
|
||||
stream->print("\n");
|
||||
}
|
||||
|
||||
void Interpolate(const Color3i& other_color, float t);
|
||||
|
||||
uint8_t* At(int rgb_index);
|
||||
const uint8_t* At(int rgb_index) const;
|
||||
|
||||
uint8_t r_, g_, b_;
|
||||
};
|
||||
|
||||
|
||||
struct ColorHSV {
|
||||
ColorHSV() : h_(0), s_(0), v_(0) {}
|
||||
ColorHSV(float h, float s, float v) {
|
||||
Set(h,s,v);
|
||||
}
|
||||
explicit ColorHSV(const Color3i& color) {
|
||||
FromRGB(color);
|
||||
}
|
||||
ColorHSV& operator=(const Color3i& color) {
|
||||
FromRGB(color);
|
||||
return *this;
|
||||
}
|
||||
ColorHSV(const ColorHSV& other) {
|
||||
Set(other);
|
||||
}
|
||||
void Set(const ColorHSV& other) {
|
||||
Set(other.h_, other.s_, other.v_);
|
||||
}
|
||||
void Set(float h, float s, float v) {
|
||||
h_ = h;
|
||||
s_ = s;
|
||||
v_ = v;
|
||||
}
|
||||
|
||||
template <typename PrintStream>
|
||||
inline void Print(PrintStream* stream) {
|
||||
stream->print("HSV:\t");
|
||||
stream->print(h_); stream->print(",\t");
|
||||
stream->print(s_); stream->print(",\t");
|
||||
stream->print(v_); stream->print("\n");
|
||||
}
|
||||
|
||||
bool operator==(const ColorHSV& other) const {
|
||||
return h_ == other.h_ && s_ == other.s_ && v_ == other.v_;
|
||||
}
|
||||
bool operator!=(const ColorHSV& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
void FromRGB(const Color3i& rgb);
|
||||
|
||||
Color3i ToRGB() const;
|
||||
|
||||
float h_, s_, v_;
|
||||
};
|
||||
|
||||
|
||||
#endif // COLOR_H_
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
#include "./color_mapper.h"
|
||||
|
||||
#include "./color.h"
|
||||
#include "./util.h"
|
||||
|
||||
// Serves as the pallet for selecting a color.
|
||||
struct ColorScheme {
|
||||
ColorScheme(const ColorHSV& c0,
|
||||
const ColorHSV& c1,
|
||||
const ColorHSV& c2,
|
||||
const ColorHSV& c3,
|
||||
const ColorHSV& c4,
|
||||
const ColorHSV& c5,
|
||||
const ColorHSV& c6,
|
||||
const ColorHSV& c7,
|
||||
const ColorHSV& c8,
|
||||
const ColorHSV& c9,
|
||||
const ColorHSV& c10,
|
||||
const ColorHSV& c11) {
|
||||
data[0] = c0;
|
||||
data[1] = c1;
|
||||
data[2] = c2;
|
||||
data[3] = c3;
|
||||
data[4] = c4;
|
||||
data[5] = c5;
|
||||
data[6] = c6;
|
||||
data[7] = c7;
|
||||
data[8] = c8;
|
||||
data[9] = c9;
|
||||
data[10] = c10;
|
||||
data[11] = c11;
|
||||
}
|
||||
ColorHSV data[12];
|
||||
};
|
||||
|
||||
const ColorScheme& SelectColorScheme(int indx) {
|
||||
static ColorScheme color_schemes[] = {
|
||||
|
||||
// Coda
|
||||
ColorScheme(
|
||||
ColorHSV(Color3i(0xff, 0x00, 0x00)), // C
|
||||
ColorHSV(Color3i(0x00, 0x80, 0xff)), // C
|
||||
ColorHSV(Color3i(0xff, 0xff, 0x00)), // D
|
||||
ColorHSV(Color3i(0x80, 0x00, 0xff)), // D#
|
||||
ColorHSV(Color3i(0x00, 0xff, 0x00)), // E
|
||||
ColorHSV(Color3i(0xff, 0x00, 0x80)), // F
|
||||
ColorHSV(Color3i(0x00, 0xff, 0xff)), // F#
|
||||
ColorHSV(Color3i(0xff, 0x80, 0x00)), // G
|
||||
ColorHSV(Color3i(0x00, 0x00, 0xff)), // G#
|
||||
ColorHSV(Color3i(0x80, 0xff, 0x00)), // A
|
||||
ColorHSV(Color3i(0xff, 0x00, 0xff)), // A#
|
||||
ColorHSV(Color3i(0x00, 0xff, 0x80))
|
||||
),
|
||||
|
||||
// Frequency
|
||||
ColorScheme(
|
||||
ColorHSV(Color3i(0xfc, 0xff, 0x00)), // C
|
||||
ColorHSV(Color3i(0x00, 0xff, 0x73)), // C#
|
||||
ColorHSV(Color3i(0x00, 0xa7, 0xff)),
|
||||
ColorHSV(Color3i(0x00, 0x20, 0xff)),
|
||||
ColorHSV(Color3i(0x35, 0x00, 0xff)),
|
||||
ColorHSV(Color3i(0x56, 0x00, 0xb6)),
|
||||
ColorHSV(Color3i(0x4e, 0x00, 0x6c)),
|
||||
ColorHSV(Color3i(0x9f, 0x00, 0x00)), // G
|
||||
ColorHSV(Color3i(0xdb, 0x00, 0x00)),
|
||||
ColorHSV(Color3i(0xff, 0x36, 0x00)), // A
|
||||
ColorHSV(Color3i(0xff, 0xc1, 0x00)),
|
||||
ColorHSV(Color3i(0xbf, 0xff, 0x00)) // B
|
||||
),
|
||||
|
||||
// SCRIABIN
|
||||
ColorScheme(
|
||||
ColorHSV(Color3i(0xff, 0x00, 0x00)), // C
|
||||
ColorHSV(Color3i(0x8f, 0x00, 0xff)), // C#
|
||||
ColorHSV(Color3i(0xff, 0xff, 0x00)), // D
|
||||
ColorHSV(Color3i(0x71, 0x63, 0x95)), // D#
|
||||
ColorHSV(Color3i(0x4f, 0xa1, 0xc2)), // E
|
||||
ColorHSV(Color3i(0xc1, 0x01, 0x01)), // F
|
||||
ColorHSV(Color3i(0x00, 0x00, 0xff)), // F#
|
||||
ColorHSV(Color3i(0xff, 0x66, 0x00)), // G
|
||||
ColorHSV(Color3i(0x96, 0x00, 0xff)), // G#
|
||||
ColorHSV(Color3i(0x00, 0xff, 0x00)), // A
|
||||
ColorHSV(Color3i(0x71, 0x63, 0x95)), // A#
|
||||
ColorHSV(Color3i(0x4f, 0xa1, 0xc2)) // B
|
||||
),
|
||||
|
||||
// LUIS BERTRAND CASTEL
|
||||
ColorScheme(
|
||||
ColorHSV(Color3i(0x00, 0x00, 0xff)), // C
|
||||
ColorHSV(Color3i(0x0d, 0x98, 0xba)), // C#
|
||||
ColorHSV(Color3i(0x00, 0xff, 0x00)), // D
|
||||
ColorHSV(Color3i(0x80, 0x80, 0x00)), // D#
|
||||
ColorHSV(Color3i(0xff, 0xff, 0x00)), // E
|
||||
ColorHSV(Color3i(0xff, 0xd7, 0x00)), // F
|
||||
ColorHSV(Color3i(0xff, 0x5a, 0x00)), // F#
|
||||
ColorHSV(Color3i(0xff, 0x00, 0x00)), // G
|
||||
ColorHSV(Color3i(0xdc, 0x14, 0x3c)), // G#
|
||||
ColorHSV(Color3i(0x8f, 0x00, 0xff)), // A
|
||||
ColorHSV(Color3i(0x22, 0x00, 0xcd)), // A#
|
||||
ColorHSV(Color3i(0x5a, 0x00, 0x95)) // B
|
||||
),
|
||||
|
||||
// H VON HELMHOHOLTZ
|
||||
ColorScheme(
|
||||
ColorHSV(Color3i(0xff, 0xff, 0x06)), // C
|
||||
ColorHSV(Color3i(0x00, 0xff, 0x00)), // C#
|
||||
ColorHSV(Color3i(0x21, 0x9e, 0xbd)), // D
|
||||
ColorHSV(Color3i(0x00, 0x80, 0xff)), // D#
|
||||
ColorHSV(Color3i(0x6f, 0x00, 0xff)), // E
|
||||
ColorHSV(Color3i(0x8f, 0x00, 0xff)), // F
|
||||
ColorHSV(Color3i(0xff, 0x00, 0x00)), // F#
|
||||
ColorHSV(Color3i(0xff, 0x20, 0x00)), // G
|
||||
ColorHSV(Color3i(0xff, 0x38, 0x00)), // G#
|
||||
ColorHSV(Color3i(0xff, 0x3f, 0x00)), // A
|
||||
ColorHSV(Color3i(0xff, 0x3f, 0x34)), // A#
|
||||
ColorHSV(Color3i(0xff, 0xa5, 0x00)) // B
|
||||
),
|
||||
|
||||
// ZIEVERINK
|
||||
ColorScheme(
|
||||
ColorHSV(Color3i(0x9a, 0xcd, 0x32)), // C
|
||||
ColorHSV(Color3i(0x00, 0xff, 0x00)), // C#
|
||||
ColorHSV(Color3i(0x00, 0xdd, 0xdd)), // D
|
||||
ColorHSV(Color3i(0x00, 0x00, 0xff)), // D#
|
||||
ColorHSV(Color3i(0x6f, 0x00, 0xff)), // E
|
||||
ColorHSV(Color3i(0x8f, 0x00, 0xff)), // F
|
||||
ColorHSV(Color3i(0x7f, 0x1a, 0xe5)), // F#
|
||||
ColorHSV(Color3i(0xbd, 0x00, 0x20)), // G
|
||||
ColorHSV(Color3i(0xff, 0x00, 0x00)), // G#
|
||||
ColorHSV(Color3i(0xff, 0x44, 0x00)), // A
|
||||
ColorHSV(Color3i(0xff, 0xc4, 0x00)), // A#
|
||||
ColorHSV(Color3i(0xff, 0xff, 0x00)) // B
|
||||
),
|
||||
|
||||
// ROSICRUCIAN ORDER
|
||||
ColorScheme(
|
||||
ColorHSV(Color3i(0x9a, 0xcd, 0x32)), // C
|
||||
ColorHSV(Color3i(0x00, 0xff, 0x00)), // C#
|
||||
ColorHSV(Color3i(0x21, 0x9e, 0xbd)), // D
|
||||
ColorHSV(Color3i(0x00, 0x00, 0xff)), // D#
|
||||
ColorHSV(Color3i(0x8a, 0x2b, 0xe2)), // E
|
||||
ColorHSV(Color3i(0x8b, 0x00, 0xff)), // F
|
||||
ColorHSV(Color3i(0xf7, 0x53, 0x94)), // F#
|
||||
ColorHSV(Color3i(0xbd, 0x00, 0x20)), // G
|
||||
ColorHSV(Color3i(0xee, 0x20, 0x4d)), // G#
|
||||
ColorHSV(Color3i(0xff, 0x3f, 0x34)), // A
|
||||
ColorHSV(Color3i(0xff, 0xa5, 0x00)), // A#
|
||||
ColorHSV(Color3i(0xff, 0xff, 0x00)) // B
|
||||
),
|
||||
};
|
||||
|
||||
static const int n = sizeof(color_schemes) / sizeof(color_schemes[0]);
|
||||
indx = constrain(indx, 0, n - 1);
|
||||
|
||||
return color_schemes[indx];
|
||||
};
|
||||
|
||||
const ColorHSV SelectColor(int midi_note, float brightness, int color_selector_val) {
|
||||
const uint8_t fun_note = FundamentalNote(midi_note);
|
||||
const ColorScheme& color_scheme = SelectColorScheme(color_selector_val);
|
||||
ColorHSV color = color_scheme.data[fun_note];
|
||||
color.v_ = brightness;
|
||||
return color;
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
|
||||
#ifndef COLOR_MAPPER_H_
|
||||
#define COLOR_MAPPER_H_
|
||||
|
||||
#include "./color.h"
|
||||
|
||||
const ColorHSV SelectColor(int midi_note, float brightness, int color_selector_val);
|
||||
|
||||
#endif // COLOR_MAPPER_H_
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#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,5 @@
|
|||
|
||||
|
||||
#include "dprint.h"
|
||||
|
||||
bool is_debugging = false;
|
||||
13
libraries/FastLED/examples/LuminescentGrand/shared/dprint.h
Normal file
13
libraries/FastLED/examples/LuminescentGrand/shared/dprint.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
|
||||
extern bool is_debugging;
|
||||
//#define ENABLE_DPRINT
|
||||
#ifdef ENABLE_DPRINT
|
||||
#define dprint(x) if (is_debugging) { Serial.print(x); }
|
||||
#define dprintln(x) if (is_debugging) { Serial.println(x); }
|
||||
#else
|
||||
#define dprint(x)
|
||||
#define dprintln(x)
|
||||
#endif
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
|
||||
#include "./framebuffer.h"
|
||||
|
||||
#include "./color.h"
|
||||
|
||||
FrameBufferBase::FrameBufferBase(Color3i* array, int n_pixels)
|
||||
: color_array_(array), n_color_array_(n_pixels) {}
|
||||
|
||||
FrameBufferBase::~FrameBufferBase() {}
|
||||
|
||||
void FrameBufferBase::Set(int i, const Color3i& c) {
|
||||
color_array_[i] = c;
|
||||
}
|
||||
void FrameBufferBase::Set(int i, int length, const Color3i& color) {
|
||||
for (int j = 0; j < length; ++j) {
|
||||
Set(i + j, color);
|
||||
}
|
||||
}
|
||||
void FrameBufferBase::FillColor(const Color3i& color) {
|
||||
for (int i = 0; i < n_color_array_; ++i) {
|
||||
color_array_[i] = color;
|
||||
}
|
||||
}
|
||||
void FrameBufferBase::ApplyBlendSubtract(const Color3i& color) {
|
||||
for (int i = 0; i < n_color_array_; ++i) {
|
||||
color_array_[i].Sub(color);
|
||||
}
|
||||
}
|
||||
void FrameBufferBase::ApplyBlendAdd(const Color3i& color) {
|
||||
for (int i = 0; i < n_color_array_; ++i) {
|
||||
color_array_[i].Add(color);
|
||||
}
|
||||
}
|
||||
void FrameBufferBase::ApplyBlendMultiply(const Color3i& color) {
|
||||
for (int i = 0; i < n_color_array_; ++i) {
|
||||
color_array_[i].Mul(color);
|
||||
}
|
||||
}
|
||||
Color3i* FrameBufferBase::GetIterator(int i) {
|
||||
return color_array_ + i;
|
||||
}
|
||||
// Length in pixels.
|
||||
int FrameBufferBase::length() const { return n_color_array_; }
|
||||
|
||||
FrameBuffer::FrameBuffer(int n_pixels)
|
||||
: FrameBufferBase(static_cast<Color3i*>(malloc(sizeof(Color3i) * n_pixels)),
|
||||
n_pixels) {
|
||||
}
|
||||
|
||||
FrameBuffer::~FrameBuffer() {
|
||||
free(color_array_);
|
||||
color_array_ = NULL;
|
||||
n_color_array_ = 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
|
||||
|
||||
#ifndef FRAME_BUFFER_H_
|
||||
#define FRAME_BUFFER_H_
|
||||
|
||||
struct Color3i;
|
||||
|
||||
class FrameBufferBase {
|
||||
public:
|
||||
FrameBufferBase(Color3i* array, int n_pixels);
|
||||
virtual ~FrameBufferBase();
|
||||
|
||||
void Set(int i, const Color3i& c);
|
||||
void Set(int i, int length, const Color3i& color);
|
||||
void FillColor(const Color3i& color);
|
||||
void ApplyBlendSubtract(const Color3i& color);
|
||||
void ApplyBlendAdd(const Color3i& color);
|
||||
void ApplyBlendMultiply(const Color3i& color);
|
||||
Color3i* GetIterator(int i);
|
||||
|
||||
// Length in pixels.
|
||||
int length() const;
|
||||
|
||||
protected:
|
||||
Color3i* color_array_;
|
||||
int n_color_array_;
|
||||
};
|
||||
|
||||
class FrameBuffer : public FrameBufferBase {
|
||||
public:
|
||||
FrameBuffer(int n_pixels);
|
||||
virtual ~FrameBuffer();
|
||||
};
|
||||
|
||||
#endif // FRAME_BUFFER_H_
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue