This sketch is fully compatible with the FastLED web compiler.
This sketch is fully compatible with the FastLED web compiler. To use it do the following:
#if !SKETCH_HAS_LOTS_OF_MEMORY
#include <Arduino.h>
Serial.begin(115200);
Serial.println("Chromancer.ino: setup() - Platform has insufficient memory for full demo");
}
Serial.println("Chromancer.ino: loop() - Platform has insufficient memory for full demo");
delay(1000);
}
#else
enum {
BlackStrip = 0,
GreenStrip = 1,
RedStrip = 2,
BlueStrip = 3,
};
constexpr int lengths[] = {
154,
168,
84,
154
};
CRGB leds0[lengths[BlackStrip]] = {};
CRGB leds1[lengths[GreenStrip]] = {};
CRGB leds2[lengths[RedStrip]] = {};
CRGB leds3[lengths[BlueStrip]] = {};
CRGB *
leds[] = {leds0, leds1, leds2, leds3};
byte ledColors[40][14][3];
UISlider sliderDecay(
"decay", .97f, .8, 1.0, .01);
#define numberOfRipples 30
Ripple ripples[numberOfRipples] = {
};
float lastIrReading;
float highestIrReading;
unsigned long
lastHeartbeat;
#define heartbeatLockout \
500
#define heartbeatDelta 300
#define lowTemperature 33.0
#define highTemperature 37.0
float lastKnownTemperature =
(lowTemperature + highTemperature) /
2.0;
#define gyroAlpha 0.9
#define gyroThreshold \
300
float gyroX, gyroY, gyroZ;
#define randomPulsesEnabled true
#define cubePulsesEnabled true
UICheckbox starburstPulsesEnabled(
"Starburst Pulses",
true);
UICheckbox simulatedBiometricsEnabled(
"Simulated Biometrics",
true);
#define autoPulseTimeout \
5000
#define randomPulseTime 2000
unsigned long lastRandomPulse;
byte lastAutoPulseNode = 255;
byte numberOfAutoPulseTypes =
randomPulsesEnabled + cubePulsesEnabled + int(starburstPulsesEnabled);
byte currentAutoPulseType = 255;
#define autoPulseChangeTime 30000
unsigned long lastAutoPulseChange;
#define simulatedHeartbeatBaseTime \
600
#define simulatedHeartbeatVariance \
200
#define simulatedEdaBaseTime 1000
#define simulatedEdaVariance 10000
unsigned long nextSimulatedHeartbeat;
unsigned long nextSimulatedEda;
bool isNodeOnBorder(byte node) {
return true;
}
}
return false;
}
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!");
UIButton simulatedHeartbeat(
"Simulated Heartbeat");
UIButton triggerStarburst(
"Trigger Starburst");
UIButton triggerRainbowCube(
"Rainbow Cube");
UIButton triggerBorderWave(
"Border Wave");
bool wasHeartbeatClicked = false;
bool wasStarburstClicked = false;
bool wasRainbowCubeClicked = false;
bool wasBorderWaveClicked = false;
bool wasSpiralClicked = false;
UIGroup effectTriggers(
"Effect Triggers", simulatedHeartbeat, triggerStarburst, triggerRainbowCube, triggerBorderWave, triggerSpiral);
UIGroup automationControls(
"Automation", starburstPulsesEnabled, simulatedBiometricsEnabled);
Serial.begin(115200);
Serial.println("*** LET'S GOOOOO ***");
Serial.println("JSON SCREENMAP");
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());
}
bool ok = true;
auto red_it = segmentMaps.find("red_segment");
ok = (red_it != segmentMaps.end()) && ok;
if (red_it != segmentMaps.end()) red = red_it->second;
auto black_it = segmentMaps.find("back_segment");
ok = (black_it != segmentMaps.end()) && ok;
if (black_it != segmentMaps.end()) black = black_it->second;
auto green_it = segmentMaps.find("green_segment");
ok = (green_it != segmentMaps.end()) && ok;
if (green_it != segmentMaps.end()) green = green_it->second;
auto blue_it = segmentMaps.find("blue_segment");
ok = (blue_it != segmentMaps.end()) && ok;
if (blue_it != segmentMaps.end()) blue = blue_it->second;
if (!ok) {
Serial.println("Failed to get all segment maps");
return;
}
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);
}
unsigned long benchmark = millis();
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++) {
}
for (int segment = 0; segment < 40; segment++) {
for (int fromBottom = 0; fromBottom < 14; fromBottom++) {
leds[strip][led] =
CRGB(ledColors[segment][fromBottom][0],
ledColors[segment][fromBottom][1],
ledColors[segment][fromBottom][2]);
}
}
for (int i = 0; i < 4; i++) {
for (int j = 0; j < lengths[i]; j++) {
}
}
}
wasHeartbeatClicked = bool(simulatedHeartbeat);
wasStarburstClicked = bool(triggerStarburst);
wasRainbowCubeClicked = bool(triggerRainbowCube);
wasBorderWaveClicked = bool(triggerBorderWave);
wasSpiralClicked = bool(triggerSpiral);
if (wasSpiralClicked) {
unsigned int baseColor = random(0xFFFF);
byte centerNode = 15;
for (int i = 0; i < 6; i++) {
for (int j = 0; j < numberOfRipples; j++) {
if (ripples[j].state ==
dead) {
centerNode, i,
baseColor + (0xFFFF / 6) * i, 255, 255),
0.3 + (i * 0.1),
2000,
break;
}
}
}
}
lastHeartbeat = millis();
}
if (wasBorderWaveClicked) {
unsigned int baseColor = random(0xFFFF);
for (int dir = 0; dir < 6; dir++) {
for (int j = 0; j < numberOfRipples; j++) {
if (ripples[j].state ==
dead) {
node, dir,
255, 255),
.4, 2000, 0);
break;
}
}
break;
}
}
}
lastHeartbeat = millis();
}
if (wasRainbowCubeClicked) {
unsigned int baseColor = random(0xFFFF);
for (int i = 0; i < 6; i++) {
for (int j = 0; j < numberOfRipples; j++) {
if (ripples[j].state ==
dead) {
node, i,
baseColor + (0xFFFF / 6) * i, 255, 255),
.5, 2000, behavior);
break;
}
}
}
}
lastHeartbeat = millis();
}
if (wasStarburstClicked) {
unsigned int baseColor = random(0xFFFF);
for (int i = 0; i < 6; i++) {
for (int j = 0; j < numberOfRipples; j++) {
if (ripples[j].state ==
dead) {
baseColor + (0xFFFF / 6) * i, 255, 255),
.65, 1500, behavior);
break;
}
}
}
lastHeartbeat = millis();
}
if (wasHeartbeatClicked) {
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) {
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;
foundStartingNode = false;
}
if (node == lastAutoPulseNode)
foundStartingNode = false;
}
lastAutoPulseNode = node;
for (int i = 0; i < 6; i++) {
for (int j = 0; j < numberOfRipples; j++) {
if (ripples[j].state ==
dead) {
node, i,
255),
float(random(100)) / 100.0 * .2 + .5, 3000,
1);
break;
}
}
}
}
break;
}
case 1: {
while (node == lastAutoPulseNode)
lastAutoPulseNode = node;
for (int i = 0; i < 6; i++) {
for (int j = 0; j < numberOfRipples; j++) {
if (ripples[j].state ==
dead) {
node, i,
255),
.5, 2000, behavior);
break;
}
}
}
}
break;
}
case 2: {
for (int i = 0; i < 6; i++) {
for (int j = 0; j < numberOfRipples; j++) {
if (ripples[j].state ==
dead) {
baseColor + (0xFFFF / 6) * i, 255, 255),
.65, 1500, behavior);
break;
}
}
}
break;
}
default:
break;
}
lastRandomPulse = millis();
}
if (simulatedBiometricsEnabled) {
if (millis() >= nextSimulatedHeartbeat) {
for (int i = 0; i < 6; i++) {
for (int j = 0; j < numberOfRipples; j++) {
if (ripples[j].state ==
dead) {
15, i, 0xEE1111,
float(random(100)) / 100.0 * .1 + .4, 1000, 0);
break;
}
}
}
nextSimulatedHeartbeat = millis() + simulatedHeartbeatBaseTime +
random(simulatedHeartbeatVariance);
}
if (millis() >= nextSimulatedEda) {
for (int i = 0; i < 10; i++) {
for (int j = 0; j < numberOfRipples; j++) {
if (ripples[j].state ==
dead) {
byte targetNode =
byte direction = 255;
while (direction == 255) {
direction = random(6);
direction = 255;
}
targetNode, direction, 0x1111EE,
float(random(100)) / 100.0 * .5 + 2, 300, 2);
break;
}
}
}
nextSimulatedEda = millis() + simulatedEdaBaseTime +
random(simulatedEdaVariance);
}
}
}
}
#endif
FL_DISABLE_WARNING_PUSH FL_DISABLE_WARNING_GLOBAL_CONSTRUCTORS CFastLED FastLED
Global LED strip management instance.
central include file for FastLED, defines the CFastLED class/object
UITitle title("Audio Reactive Visualizations")
UIDescription description("Real-time audio visualizations with beat detection and multiple modes")
void start(byte n, byte d, unsigned long c, float s, unsigned long l, byte b)
void advance(byte ledColors[40][14][3])
static bool ParseJson(const char *jsonStrScreenMap, fl::fl_map< string, ScreenMap > *segmentMaps, string *err=nullptr)
uint32_t Adafruit_DotStar_ColorHSV(uint16_t hue, uint8_t sat, uint8_t val)
UICheckbox allWhite("All White", false)
FastLED's Elegant JSON Library: fl::Json
int nodeConnections[25][6]
int ledAssignments[40][3]
void printf(const char *format, const Args &... args)
Printf-like formatting function that prints directly to the platform output.
MapRedBlackTree< Key, T, Compare, fl::allocator_slab< char > > fl_map
float fmap(float x, float in_min, float in_max, float out_min, float out_max)
const char JSON_SCREEN_MAP[]
@ White
<div style='background:#FFFFFF;width:4em;height:4em;'></div>
Representation of an RGB pixel (Red, Green, Blue)
UIGroup displayControls("Display Controls", brightness, isOff, timeSpeed)