26#if !SKETCH_HAS_LOTS_OF_MEMORY
34 Serial.println(
"Chromancer.ino: setup() - Platform has insufficient memory for full demo");
38 Serial.println(
"Chromancer.ino: loop() - Platform has insufficient memory for full demo");
71constexpr int lengths[] = {
86CRGB leds0[lengths[BlackStrip]] = {};
87CRGB leds1[lengths[GreenStrip]] = {};
88CRGB leds2[lengths[RedStrip]] = {};
89CRGB leds3[lengths[BlueStrip]] = {};
90CRGB *
leds[] = {leds0, leds1, leds2, leds3};
93byte ledColors[40][14][3];
98UISlider sliderDecay(
"decay", .97f, .8, 1.0, .01);
102#define numberOfRipples 30
103Ripple ripples[numberOfRipples] = {
114float highestIrReading;
117#define heartbeatLockout \
119#define heartbeatDelta 300
122#define lowTemperature 33.0
123#define highTemperature 37.0
124float lastKnownTemperature =
125 (lowTemperature + highTemperature) /
133#define gyroThreshold \
135float gyroX, gyroY, gyroZ;
139#define randomPulsesEnabled true
140#define cubePulsesEnabled true
141UICheckbox starburstPulsesEnabled(
"Starburst Pulses",
true);
142UICheckbox simulatedBiometricsEnabled(
"Simulated Biometrics",
true);
144#define autoPulseTimeout \
147#define randomPulseTime 2000
148unsigned long lastRandomPulse;
149byte lastAutoPulseNode = 255;
151byte numberOfAutoPulseTypes =
152 randomPulsesEnabled + cubePulsesEnabled + int(starburstPulsesEnabled);
153byte currentAutoPulseType = 255;
154#define autoPulseChangeTime 30000
155unsigned long lastAutoPulseChange;
157#define simulatedHeartbeatBaseTime \
159#define simulatedHeartbeatVariance \
161#define simulatedEdaBaseTime 1000
162#define simulatedEdaVariance 10000
163unsigned long nextSimulatedHeartbeat;
164unsigned long nextSimulatedEda;
167bool isNodeOnBorder(
byte node) {
177UIDescription 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!");
180UIButton simulatedHeartbeat(
"Simulated Heartbeat");
181UIButton triggerStarburst(
"Trigger Starburst");
182UIButton triggerRainbowCube(
"Rainbow Cube");
183UIButton triggerBorderWave(
"Border Wave");
184UIButton triggerSpiral(
"Spiral Wave");
185bool wasHeartbeatClicked =
false;
186bool wasStarburstClicked =
false;
187bool wasRainbowCubeClicked =
false;
188bool wasBorderWaveClicked =
false;
189bool wasSpiralClicked =
false;
192UIGroup effectTriggers(
"Effect Triggers", simulatedHeartbeat, triggerStarburst, triggerRainbowCube, triggerBorderWave, triggerSpiral);
193UIGroup automationControls(
"Automation", starburstPulsesEnabled, simulatedBiometricsEnabled);
197 Serial.begin(115200);
199 Serial.println(
"*** LET'S GOOOOO ***");
201 Serial.println(
"JSON SCREENMAP");
207 printf(
"Parsed %d segment maps\n",
int(segmentMaps.
size()));
208 for (
auto kv : segmentMaps) {
209 Serial.print(kv.first.c_str());
211 Serial.println(kv.second.getLength());
219 auto red_it = segmentMaps.find(
"red_segment");
220 ok = (red_it != segmentMaps.end()) && ok;
221 if (red_it != segmentMaps.end()) red = red_it->second;
223 auto black_it = segmentMaps.find(
"back_segment");
224 ok = (black_it != segmentMaps.end()) && ok;
225 if (black_it != segmentMaps.end()) black = black_it->second;
227 auto green_it = segmentMaps.find(
"green_segment");
228 ok = (green_it != segmentMaps.end()) && ok;
229 if (green_it != segmentMaps.end()) green = green_it->second;
231 auto blue_it = segmentMaps.find(
"blue_segment");
232 ok = (blue_it != segmentMaps.end()) && ok;
233 if (blue_it != segmentMaps.end()) blue = blue_it->second;
235 Serial.println(
"Failed to get all segment maps");
241 CRGB* black_leds =
leds[BlackStrip];
242 CRGB* green_leds =
leds[GreenStrip];
245 FastLED.addLeds<
WS2812, 2>(black_leds, lengths[BlackStrip]).setScreenMap(black);
246 FastLED.addLeds<
WS2812, 3>(green_leds, lengths[GreenStrip]).setScreenMap(green);
247 FastLED.addLeds<
WS2812, 1>(red_leds, lengths[RedStrip]).setScreenMap(red);
248 FastLED.addLeds<
WS2812, 4>(blue_leds, lengths[BlueStrip]).setScreenMap(blue);
255 unsigned long benchmark = millis();
259 for (
int strip = 0; strip < 40; strip++) {
260 for (
int led = 0; led < 14; led++) {
261 for (
int i = 0; i < 3; i++) {
262 ledColors[strip][led][i] *= sliderDecay.value();
267 for (
int i = 0; i < numberOfRipples; i++) {
271 for (
int segment = 0; segment < 40; segment++) {
272 for (
int fromBottom = 0; fromBottom < 14; fromBottom++) {
276 leds[strip][led] =
CRGB(ledColors[segment][fromBottom][0],
277 ledColors[segment][fromBottom][1],
278 ledColors[segment][fromBottom][2]);
284 for (
int i = 0; i < 4; i++) {
285 for (
int j = 0; j < lengths[i]; j++) {
295 wasHeartbeatClicked = bool(simulatedHeartbeat);
296 wasStarburstClicked = bool(triggerStarburst);
297 wasRainbowCubeClicked = bool(triggerRainbowCube);
298 wasBorderWaveClicked = bool(triggerBorderWave);
299 wasSpiralClicked = bool(triggerSpiral);
301 if (wasSpiralClicked) {
303 unsigned int baseColor = random(0xFFFF);
304 byte centerNode = 15;
307 for (
int i = 0; i < 6; i++) {
309 for (
int j = 0; j < numberOfRipples; j++) {
310 if (ripples[j].state ==
dead) {
314 baseColor + (0xFFFF / 6) * i, 255, 255),
323 lastHeartbeat = millis();
326 if (wasBorderWaveClicked) {
328 unsigned int baseColor = random(0xFFFF);
334 for (
int dir = 0; dir < 6; dir++) {
337 for (
int j = 0; j < numberOfRipples; j++) {
338 if (ripples[j].state ==
dead) {
352 lastHeartbeat = millis();
355 if (wasRainbowCubeClicked) {
358 unsigned int baseColor = random(0xFFFF);
361 for (
int i = 0; i < 6; i++) {
363 for (
int j = 0; j < numberOfRipples; j++) {
364 if (ripples[j].state ==
dead) {
368 baseColor + (0xFFFF / 6) * i, 255, 255),
375 lastHeartbeat = millis();
378 if (wasStarburstClicked) {
380 unsigned int baseColor = random(0xFFFF);
383 for (
int i = 0; i < 6; i++) {
384 for (
int j = 0; j < numberOfRipples; j++) {
385 if (ripples[j].state ==
dead) {
389 baseColor + (0xFFFF / 6) * i, 255, 255),
390 .65, 1500, behavior);
395 lastHeartbeat = millis();
398 if (wasHeartbeatClicked) {
400 for (
int i = 0; i < 6; i++) {
401 for (
int j = 0; j < numberOfRipples; j++) {
402 if (ripples[j].state ==
dead) {
403 ripples[j].
start(15, i, 0xEE1111,
404 float(random(100)) / 100.0 * .1 + .4, 1000, 0);
409 lastHeartbeat = millis();
412 if (millis() - lastHeartbeat >= autoPulseTimeout) {
414 if (numberOfAutoPulseTypes &&
415 millis() - lastRandomPulse >= randomPulseTime) {
416 unsigned int baseColor = random(0xFFFF);
418 if (currentAutoPulseType == 255 ||
419 (numberOfAutoPulseTypes > 1 &&
420 millis() - lastAutoPulseChange >= autoPulseChangeTime)) {
421 byte possiblePulse = 255;
423 possiblePulse = random(3);
425 if (possiblePulse == currentAutoPulseType)
428 switch (possiblePulse) {
430 if (!randomPulsesEnabled)
435 if (!cubePulsesEnabled)
440 if (!starburstPulsesEnabled)
448 currentAutoPulseType = possiblePulse;
449 lastAutoPulseChange = millis();
454 switch (currentAutoPulseType) {
457 bool foundStartingNode =
false;
458 while (!foundStartingNode) {
460 foundStartingNode =
true;
465 foundStartingNode =
false;
468 if (node == lastAutoPulseNode)
469 foundStartingNode =
false;
472 lastAutoPulseNode = node;
474 for (
int i = 0; i < 6; i++) {
476 for (
int j = 0; j < numberOfRipples; j++) {
477 if (ripples[j].state ==
dead) {
485 float(random(100)) / 100.0 * .2 + .5, 3000,
499 while (node == lastAutoPulseNode)
502 lastAutoPulseNode = node;
506 for (
int i = 0; i < 6; i++) {
508 for (
int j = 0; j < numberOfRipples; j++) {
509 if (ripples[j].state ==
dead) {
532 for (
int i = 0; i < 6; i++) {
533 for (
int j = 0; j < numberOfRipples; j++) {
534 if (ripples[j].state ==
dead) {
538 baseColor + (0xFFFF / 6) * i, 255, 255),
539 .65, 1500, behavior);
551 lastRandomPulse = millis();
554 if (simulatedBiometricsEnabled) {
556 if (millis() >= nextSimulatedHeartbeat) {
557 for (
int i = 0; i < 6; i++) {
558 for (
int j = 0; j < numberOfRipples; j++) {
559 if (ripples[j].state ==
dead) {
562 float(random(100)) / 100.0 * .1 + .4, 1000, 0);
569 nextSimulatedHeartbeat = millis() + simulatedHeartbeatBaseTime +
570 random(simulatedHeartbeatVariance);
574 if (millis() >= nextSimulatedEda) {
575 for (
int i = 0; i < 10; i++) {
576 for (
int j = 0; j < numberOfRipples; j++) {
577 if (ripples[j].state ==
dead) {
580 byte direction = 255;
582 while (direction == 255) {
583 direction = random(6);
589 targetNode, direction, 0x1111EE,
590 float(random(100)) / 100.0 * .5 + 2, 300, 2);
597 nextSimulatedEda = millis() + simulatedEdaBaseTime +
598 random(simulatedEdaVariance);
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)