13#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)
25#include "fl/screenmap.h"
26#include "fl/math_macros.h"
31#include "screenmap.json.h"
47constexpr int lengths[] = {
62CRGB leds0[lengths[BlackStrip]] = {};
63CRGB leds1[lengths[GreenStrip]] = {};
64CRGB leds2[lengths[RedStrip]] = {};
65CRGB leds3[lengths[BlueStrip]] = {};
66CRGB *leds[] = {leds0, leds1, leds2, leds3};
69byte ledColors[40][14][3];
74UISlider sliderDecay(
"decay", .97f, .8, 1.0, .01);
78#define numberOfRipples 30
79Ripple ripples[numberOfRipples] = {
90float highestIrReading;
93#define heartbeatLockout \
95#define heartbeatDelta 300
98#define lowTemperature 33.0
99#define highTemperature 37.0
100float lastKnownTemperature =
101 (lowTemperature + highTemperature) /
109#define gyroThreshold \
111float gyroX, gyroY, gyroZ;
115#define randomPulsesEnabled true
116#define cubePulsesEnabled true
117UICheckbox starburstPulsesEnabled(
"Starburst Pulses",
true);
118UICheckbox simulatedBiometricsEnabled(
"Simulated Biometrics",
true);
120#define autoPulseTimeout \
123#define randomPulseTime 2000
124unsigned long lastRandomPulse;
125byte lastAutoPulseNode = 255;
127byte numberOfAutoPulseTypes =
128 randomPulsesEnabled + cubePulsesEnabled + int(starburstPulsesEnabled);
129byte currentAutoPulseType = 255;
130#define autoPulseChangeTime 30000
131unsigned long lastAutoPulseChange;
133#define simulatedHeartbeatBaseTime \
135#define simulatedHeartbeatVariance \
137#define simulatedEdaBaseTime 1000
138#define simulatedEdaVariance 10000
139unsigned long nextSimulatedHeartbeat;
140unsigned long nextSimulatedEda;
143bool isNodeOnBorder(
byte node) {
144 for (
int i = 0; i < numberOfBorderNodes; i++) {
145 if (node == borderNodes[i]) {
153UIDescription 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!");
156UIButton simulatedHeartbeat(
"Simulated Heartbeat");
157UIButton triggerStarburst(
"Trigger Starburst");
158UIButton triggerRainbowCube(
"Rainbow Cube");
159UIButton triggerBorderWave(
"Border Wave");
160UIButton triggerSpiral(
"Spiral Wave");
161bool wasHeartbeatClicked =
false;
162bool wasStarburstClicked =
false;
163bool wasRainbowCubeClicked =
false;
164bool wasBorderWaveClicked =
false;
165bool wasSpiralClicked =
false;
168 Serial.begin(115200);
170 Serial.println(
"*** LET'S GOOOOO ***");
172 Serial.println(
"JSON SCREENMAP");
173 Serial.println(JSON_SCREEN_MAP);
176 ScreenMap::ParseJson(JSON_SCREEN_MAP, &segmentMaps);
178 printf(
"Parsed %d segment maps\n",
int(segmentMaps.size()));
179 for (
auto kv : segmentMaps) {
180 Serial.print(kv.first.c_str());
182 Serial.println(kv.second.getLength());
189 ok = segmentMaps.get(
"red_segment", &red) && ok;
190 ok = segmentMaps.get(
"back_segment", &black) && ok;
191 ok = segmentMaps.get(
"green_segment", &green) && ok;
192 ok = segmentMaps.get(
"blue_segment", &blue) && ok;
194 Serial.println(
"Failed to get all segment maps");
199 CRGB* red_leds = leds[RedStrip];
200 CRGB* black_leds = leds[BlackStrip];
201 CRGB* green_leds = leds[GreenStrip];
202 CRGB* blue_leds = leds[BlueStrip];
215 unsigned long benchmark = millis();
221 for (
int strip = 0; strip < 40; strip++) {
222 for (
int led = 0; led < 14; led++) {
223 for (
int i = 0; i < 3; i++) {
224 ledColors[strip][led][i] *= sliderDecay.value();
229 for (
int i = 0; i < numberOfRipples; i++) {
230 ripples[i].advance(ledColors);
233 for (
int segment = 0; segment < 40; segment++) {
234 for (
int fromBottom = 0; fromBottom < 14; fromBottom++) {
235 int strip = ledAssignments[segment][0];
236 int led = round(fmap(fromBottom, 0, 13, ledAssignments[segment][2],
237 ledAssignments[segment][1]));
238 leds[strip][led] =
CRGB(ledColors[segment][fromBottom][0],
239 ledColors[segment][fromBottom][1],
240 ledColors[segment][fromBottom][2]);
246 for (
int i = 0; i < 4; i++) {
247 for (
int j = 0; j < lengths[i]; j++) {
257 wasHeartbeatClicked = bool(simulatedHeartbeat);
258 wasStarburstClicked = bool(triggerStarburst);
259 wasRainbowCubeClicked = bool(triggerRainbowCube);
260 wasBorderWaveClicked = bool(triggerBorderWave);
261 wasSpiralClicked = bool(triggerSpiral);
263 if (wasSpiralClicked) {
265 unsigned int baseColor = random(0xFFFF);
266 byte centerNode = 15;
269 for (
int i = 0; i < 6; i++) {
270 if (nodeConnections[centerNode][i] >= 0) {
271 for (
int j = 0; j < numberOfRipples; j++) {
272 if (ripples[j].state == dead) {
275 Adafruit_DotStar_ColorHSV(
276 baseColor + (0xFFFF / 6) * i, 255, 255),
279 i % 2 ? alwaysTurnsLeft : alwaysTurnsRight);
285 lastHeartbeat = millis();
288 if (wasBorderWaveClicked) {
290 unsigned int baseColor = random(0xFFFF);
293 for (
int i = 0; i < numberOfBorderNodes; i++) {
294 byte node = borderNodes[i];
296 for (
int dir = 0; dir < 6; dir++) {
297 if (nodeConnections[node][dir] >= 0 &&
298 !isNodeOnBorder(nodeConnections[node][dir])) {
299 for (
int j = 0; j < numberOfRipples; j++) {
300 if (ripples[j].state == dead) {
303 Adafruit_DotStar_ColorHSV(
304 baseColor + (0xFFFF / numberOfBorderNodes) * i,
314 lastHeartbeat = millis();
317 if (wasRainbowCubeClicked) {
319 int node = cubeNodes[random(numberOfCubeNodes)];
320 unsigned int baseColor = random(0xFFFF);
321 byte behavior = random(2) ? alwaysTurnsLeft : alwaysTurnsRight;
323 for (
int i = 0; i < 6; i++) {
324 if (nodeConnections[node][i] >= 0) {
325 for (
int j = 0; j < numberOfRipples; j++) {
326 if (ripples[j].state == dead) {
329 Adafruit_DotStar_ColorHSV(
330 baseColor + (0xFFFF / 6) * i, 255, 255),
337 lastHeartbeat = millis();
340 if (wasStarburstClicked) {
342 unsigned int baseColor = random(0xFFFF);
343 byte behavior = random(2) ? alwaysTurnsLeft : alwaysTurnsRight;
345 for (
int i = 0; i < 6; i++) {
346 for (
int j = 0; j < numberOfRipples; j++) {
347 if (ripples[j].state == dead) {
350 Adafruit_DotStar_ColorHSV(
351 baseColor + (0xFFFF / 6) * i, 255, 255),
352 .65, 1500, behavior);
357 lastHeartbeat = millis();
360 if (wasHeartbeatClicked) {
362 for (
int i = 0; i < 6; i++) {
363 for (
int j = 0; j < numberOfRipples; j++) {
364 if (ripples[j].state == dead) {
365 ripples[j].start(15, i, 0xEE1111,
366 float(random(100)) / 100.0 * .1 + .4, 1000, 0);
371 lastHeartbeat = millis();
374 if (millis() - lastHeartbeat >= autoPulseTimeout) {
376 if (numberOfAutoPulseTypes &&
377 millis() - lastRandomPulse >= randomPulseTime) {
378 unsigned int baseColor = random(0xFFFF);
380 if (currentAutoPulseType == 255 ||
381 (numberOfAutoPulseTypes > 1 &&
382 millis() - lastAutoPulseChange >= autoPulseChangeTime)) {
383 byte possiblePulse = 255;
385 possiblePulse = random(3);
387 if (possiblePulse == currentAutoPulseType)
390 switch (possiblePulse) {
392 if (!randomPulsesEnabled)
397 if (!cubePulsesEnabled)
402 if (!starburstPulsesEnabled)
410 currentAutoPulseType = possiblePulse;
411 lastAutoPulseChange = millis();
416 switch (currentAutoPulseType) {
419 bool foundStartingNode =
false;
420 while (!foundStartingNode) {
422 foundStartingNode =
true;
423 for (
int i = 0; i < numberOfBorderNodes; i++) {
426 if (node == borderNodes[i])
427 foundStartingNode =
false;
430 if (node == lastAutoPulseNode)
431 foundStartingNode =
false;
434 lastAutoPulseNode = node;
436 for (
int i = 0; i < 6; i++) {
437 if (nodeConnections[node][i] >= 0) {
438 for (
int j = 0; j < numberOfRipples; j++) {
439 if (ripples[j].state == dead) {
445 Adafruit_DotStar_ColorHSV(baseColor, 255,
447 float(random(100)) / 100.0 * .2 + .5, 3000,
459 int node = cubeNodes[random(numberOfCubeNodes)];
461 while (node == lastAutoPulseNode)
462 node = cubeNodes[random(numberOfCubeNodes)];
464 lastAutoPulseNode = node;
466 byte behavior = random(2) ? alwaysTurnsLeft : alwaysTurnsRight;
468 for (
int i = 0; i < 6; i++) {
469 if (nodeConnections[node][i] >= 0) {
470 for (
int j = 0; j < numberOfRipples; j++) {
471 if (ripples[j].state == dead) {
477 Adafruit_DotStar_ColorHSV(baseColor, 255,
490 byte behavior = random(2) ? alwaysTurnsLeft : alwaysTurnsRight;
492 lastAutoPulseNode = starburstNode;
494 for (
int i = 0; i < 6; i++) {
495 for (
int j = 0; j < numberOfRipples; j++) {
496 if (ripples[j].state == dead) {
499 Adafruit_DotStar_ColorHSV(
500 baseColor + (0xFFFF / 6) * i, 255, 255),
501 .65, 1500, behavior);
513 lastRandomPulse = millis();
516 if (simulatedBiometricsEnabled) {
518 if (millis() >= nextSimulatedHeartbeat) {
519 for (
int i = 0; i < 6; i++) {
520 for (
int j = 0; j < numberOfRipples; j++) {
521 if (ripples[j].state == dead) {
524 float(random(100)) / 100.0 * .1 + .4, 1000, 0);
531 nextSimulatedHeartbeat = millis() + simulatedHeartbeatBaseTime +
532 random(simulatedHeartbeatVariance);
536 if (millis() >= nextSimulatedEda) {
537 for (
int i = 0; i < 10; i++) {
538 for (
int j = 0; j < numberOfRipples; j++) {
539 if (ripples[j].state == dead) {
541 borderNodes[random(numberOfBorderNodes)];
542 byte direction = 255;
544 while (direction == 255) {
545 direction = random(6);
546 if (nodeConnections[targetNode][direction] < 0)
551 targetNode, direction, 0x1111EE,
552 float(random(100)) / 100.0 * .5 + 2, 300, 2);
559 nextSimulatedEda = millis() + simulatedEdaBaseTime +
560 random(simulatedEdaVariance);
CFastLED FastLED
Global LED strip management instance.
central include file for FastLED, defines the CFastLED class/object
void show(uint8_t scale)
Update all our controllers with the current led colors, using the passed in brightness.
static CLEDController & addLeds(CLEDController *pLed, struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset=0)
Add a CLEDController instance to the world.
Implements a simple red square effect for 2D LED grids.
Representation of an RGB pixel (Red, Green, Blue)