BurningWreck

FastLED - LEDs blinking past end of array?

Sep 11th, 2025
579
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 10.79 KB | Software | 0 0
  1. #include "BottangoArduinoCallbacks.h"
  2. #include "src/AbstractEffector.h"
  3. #include "src/Outgoing.h"
  4. #include "src/BottangoCore.h"
  5.  
  6. // FastLED setup
  7. #include "FastLED.h"
  8. #define DATA_PIN 14
  9. #define LED_TYPE NEOPIXEL
  10. #define COLOR_ORDER GRB
  11. #define NUM_LEDS 8  // 12 - ring.  36 - strip for Evil Pumpkin
  12. #define BRIGHTNESS 50
  13. //CRGB leds[NUM_LEDS];
  14.  
  15. CRGBArray<NUM_LEDS> leds;
  16.  
  17. CRGBSet eyeLEDS(leds(6, 7));  // Eyes are last two in strand
  18. CRGBSet allLEDs(leds(0, NUM_LEDS - 1));  // This covers the entire string (zero indexed)
  19. CRGBSet randomLeds(leds(0, 5));
  20. //CRGBSet randomLeds(leds(2, 7));  // Random LEDs at end of strand, eyes are first two
  21.  
  22. // For random blinking effect
  23. #define NUM_RANDOM_LEDS 6
  24.  
  25. // Define base timing values for random blinking
  26. #define CHECK_INTERVAL 50    // How often we check for updates
  27. #define MIN_BLINK_TIME 300   // Minimum time in milliseconds
  28. #define MAX_BLINK_TIME 2000  // Maximum time in milliseconds
  29.  
  30. // Arrays to track state for each random blinking LED
  31. bool blinkState[NUM_RANDOM_LEDS];
  32. CRGB colors[NUM_RANDOM_LEDS];
  33. uint16_t nextChangeTime[NUM_RANDOM_LEDS];  // How many milliseconds until this LED changes state
  34.  
  35. bool RBlinks_animating = false;  // Controls FastLED effect on/off
  36.  
  37.  
  38. namespace Callbacks {
  39. // !!!!!!!!!!!!!!! //
  40. // !! CONTROLLER LIFECYCLE CALLBACKS !! //
  41. // !!!!!!!!!!!!!!! //
  42.  
  43. // called AFTER a successful handshake with the Bottango application, signifying that this controller has started.
  44. // use for general case startup process
  45. // Effector registration will happen after this callback, in their own callback.
  46. // If you have effector registration specific needs, you should use onEffectorRegistered
  47. void onThisControllerStarted() {
  48.   // pinMode(BUTTON_PIN, INPUT_PULLUP);  // set the button pin to be read, no pullup with touch sensor
  49.   FastLED.addLeds<LED_TYPE, DATA_PIN>(leds, NUM_LEDS);
  50.   FastLED.setBrightness(BRIGHTNESS);
  51.  
  52.   // Initialize random seed
  53.   random16_add_entropy(analogRead(35));
  54.  
  55.   // Initialize random blink timings, states, and colors
  56.   for (int i = 0; i < NUM_RANDOM_LEDS; i++) {
  57.     // Randomize initial states with variation to avoid clusters
  58.     blinkState[i] = (random8(10) > 5 + i % 3) ? true : false;
  59.  
  60.     // Assign an initial delay before the first change
  61.     nextChangeTime[i] = random16(MIN_BLINK_TIME, MAX_BLINK_TIME);
  62.  
  63.     // Generate a random color for each LED
  64.     colors[i] = CHSV(random8(), random8(180, 255), random8(200, 255));
  65.   }
  66.  
  67.   //leds(0, 1) = CRGB::Black;  // Eyes are first two in strand
  68.   leds(0, NUM_LEDS - 1) = CRGB::Black;  // NUM_LEDS - all; 1 - just the first two for eyes
  69. }
  70.  
  71. // called after the controller recieves a stop command. The controller will stop all movement, deregister all effectors
  72. // After which this call back is triggered.
  73. void onThisControllerStopped() {
  74. }
  75.  
  76. // called each loop cycle. If you have timing based code you'd like to utilize outside of the Bottango animation
  77. // This callback occurs BEFORE all effectors process their movement, at the end of the loop.
  78. void onEarlyLoop() {
  79.   // Example for triggering animations in your own logic, while in offline (save to code / SD card) mode
  80.   // if not playing anything, play animation index 2 (the third exported animation) and set it to looping
  81.  
  82.   // if (BottangoCore::commandStreamProvider->streamIsInProgress() == false)
  83.   // {
  84.   //     BottangoCore::commandStreamProvider->startCommandStream(2, true);
  85.   // }
  86.   //
  87.   // The following will stop an offline animation from playing if any
  88.   // BottangoCore::commandStreamProvider->stop();
  89. }
  90.  
  91. // called each loop cycle.
  92. // This callback occurs AFTER all effectors process their movement, at the end of the loop.
  93. void onLateLoop() {
  94.   if (RBlinks_animating == true) {
  95.  
  96.     EVERY_N_MILLISECONDS(CHECK_INTERVAL) {
  97.  
  98.       for (int i = 0; i < NUM_RANDOM_LEDS; i++) {
  99.         // Decrease the time until next change
  100.         if (nextChangeTime[i] > CHECK_INTERVAL) {
  101.           nextChangeTime[i] -= CHECK_INTERVAL;
  102.         } else {
  103.           // Time to change this LED's state
  104.           blinkState[i] = !blinkState[i];
  105.  
  106.           if (blinkState[i]) {
  107.             // Generate a new random color when turning on
  108.             colors[i] = CHSV(random8(), random8(180, 255), random8(200, 255));
  109.             // Assign new time for how long to stay ON
  110.             nextChangeTime[i] = random16(MIN_BLINK_TIME, MAX_BLINK_TIME);
  111.           } else {
  112.             // Assign new time for how long to stay OFF (slightly different range)
  113.             nextChangeTime[i] = random16(MIN_BLINK_TIME + 100, MAX_BLINK_TIME - 200);
  114.           }
  115.         }
  116.  
  117.         // Set LED to either its color or black based on current state
  118.         randomLeds[i] = blinkState[i] ? colors[i] : CRGB::Black;
  119.       }
  120.     }
  121.  
  122.     // Update the LED strip
  123.     EVERY_N_MILLISECONDS(20) {
  124.       FastLED.show();
  125.     }
  126.   }
  127.  
  128.   if (RBlinks_animating == false) {
  129.     randomLeds = CRGB::Black;  // Turn them off
  130.   }
  131.   FastLED.show();
  132.  
  133. }
  134.  
  135. // !!!!!!!!!!!!!!! //
  136. // !! EFFECTOR CALLBACKS !! //
  137. // !!!!!!!!!!!!!!! //
  138.  
  139. // All effectors have an identifier. It is an 8 char or less string. Check Bottango to see the identifier for a given effector in app.
  140. // for most effectors, it is the first pin in their set of pins
  141. // i2c effectors have the i2c address before the first pin
  142. // you query for an effector with a c string char array, instanitated at 9 characters (8 for the identifier, and a null terminating char)
  143.  
  144. // The below are called by built in effectors at various stages in their lifecycle
  145.  
  146. // called by an effector when registered, after registration is complete
  147. void onEffectorRegistered(AbstractEffector *effector) {
  148.   // example, turn on built in LED if effector registered with identifier "1";
  149.  
  150.   char effectorIdentifier[9];
  151.   effector->getIdentifier(effectorIdentifier, 9);
  152.  
  153.   if (strcmp(effectorIdentifier, "eyeColor") == 0) {
  154.     FastLED.addLeds<LED_TYPE, DATA_PIN>(leds, NUM_LEDS);
  155.     FastLED.setBrightness(BRIGHTNESS);
  156.   }
  157. }
  158.  
  159. // called by an effector when deregistered, before deregistration is complete
  160. void onEffectorDeregistered(AbstractEffector *effector) {
  161.   // example, turn off built in LED if effector registered with identifier "1";
  162.  
  163.   // char effectorIdentifier[9];
  164.   // effector->getIdentifier(effectorIdentifier, 9);
  165.  
  166.   // if (strcmp(effectorIdentifier, "1") == 0)
  167.   // {
  168.   //     pinMode(LED_BUILTIN, OUTPUT);
  169.   //     digitalWrite(LED_BUILTIN, LOW);
  170.   // }
  171. }
  172.  
  173. // called by effectors each loop with its current signal (example: servo PWM or stepper steps from home )
  174. // didChange is true if different from last update called
  175. void effectorSignalOnLoop(AbstractEffector *effector, int signal, bool didChange) {
  176.   // example, set built in led for effector with identifier "1" based on if signal is greater than 1500
  177.  
  178.   // char effectorIdentifier[9];
  179.   // effector->getIdentifier(effectorIdentifier, 9);
  180.  
  181.   // if (strcmp(effectorIdentifier, "1") == 0)
  182.   // {
  183.   //     pinMode(LED_BUILTIN, OUTPUT);
  184.   //     if (signal > 1500)
  185.   //     {
  186.   //         digitalWrite(LED_BUILTIN, HIGH);
  187.   //     }
  188.   //     else
  189.   //     {
  190.   //         digitalWrite(LED_BUILTIN, LOW);
  191.   //     }
  192.   // }
  193.  
  194.   // another example, drive a custom motor, which you have coded to have a setSignal function
  195.   // if (strcmp(effectorIdentifier, "myMotor") == 0)
  196.   // {
  197.   //      myMotor->setSignal(signal);
  198.   // }
  199. }
  200.  
  201. // !!!!!!!!!!!!!!!!!!! //
  202. // !! CUSTOM EVENTS !! //
  203. // !!!!!!!!!!!!!!!!!!! //
  204. // The below are called by custom events so you can provide your own behaviours
  205.  
  206. // called by a curved custom event any time the movement value is changed during a curved movement. (Movement is a normalized float between 0.0 - 1.0)
  207. void onCurvedCustomEventMovementChanged(AbstractEffector *effector, float newMovement) {
  208.   // example, fade an led based on the new movement value
  209.   // char effectorIdentifier[9];
  210.   // effector->getIdentifier(effectorIdentifier, 9);
  211.  
  212.   // if (strcmp(effectorIdentifier, "myLight") == 0)
  213.   // {
  214.   //     pinMode(5, OUTPUT);
  215.   //     int brightness = 255 * newMovement;
  216.   //     analogWrite(5, brightness);
  217.   // }
  218. }
  219.  
  220. // called by a on off custom event any time the on off value is changed.
  221. void onOnOffCustomEventOnOffChanged(AbstractEffector *effector, bool on) {
  222.   char effectorIdentifier[9];
  223.   effector->getIdentifier(effectorIdentifier, 9);
  224.  
  225.   if (strcmp(effectorIdentifier, "RBlinks") == 0) {  // Edit the Identifier ("RBlinks") to match On/Off event
  226.     RBlinks_animating = !RBlinks_animating;
  227.   }
  228. }
  229.  
  230. // called by a trigger custom event any time the on event is triggered.
  231. void onTriggerCustomEventTriggered(AbstractEffector *effector) {
  232.   // example, set led to a random brightness each trigger
  233.   // char effectorIdentifier[9];
  234.   // effector->getIdentifier(effectorIdentifier, 9);
  235.  
  236.   // if (strcmp(effectorIdentifier, "myLight") == 0)
  237.   // {
  238.   //     pinMode(5, OUTPUT);
  239.   //     int brightness = random(0, 256);
  240.   //     analogWrite(5, brightness);
  241.   // }
  242. }
  243.  
  244. void onColorCustomEventColorChanged(AbstractEffector *effector, byte newRed, byte newGreen, byte newBlue) {
  245.   char effectorIdentifier[9];
  246.   effector->getIdentifier(effectorIdentifier, 9);
  247.  
  248.   if (strcmp(effectorIdentifier, "eyeColor") == 0) {
  249.  
  250.     fill_solid(eyeLEDS, 2, CRGB(newRed, newGreen, newBlue));  // The "2" is for the eyes.
  251.     //fill_solid(allLEDs, 12, CRGB(newRed, newGreen, newBlue));  //  12 - ring; 36 - strip
  252.     FastLED.show();
  253.   }
  254. }
  255.  
  256. bool isStepperAutoHomeComplete(AbstractEffector *effector, int &postAutoSyncMove) {
  257.   // return true if the given stepper is at home position
  258.   // else return false
  259.  
  260.   // example, end homing on stepper with step on pin 6, when pin 10 is read high
  261.  
  262.   // postAutoSyncMove is additional steps you'd like to take after auto home is complete
  263.   // as an example, if you want to hit a limit switch, then move 200 steps clockwise
  264.   // set postAutoSyncMove = 200 and return true when the limit switch is hit
  265.   // set postAutoSyncMove to a negative number to step counter clockwise
  266.  
  267.   // char effectorIdentifier[9];
  268.   // effector->getIdentifier(effectorIdentifier, 9);
  269.  
  270.   // if (strcmp(effectorIdentifier, "6") == 0)
  271.   // {
  272.   //     pinMode(10, INPUT);
  273.   //     if (digitalRead(10) == HIGH)
  274.   //     {
  275.   //         return true;
  276.   //     }
  277.   // }
  278.  
  279.   return false;
  280. }
  281.  
  282. void onStepperPostAutoHomeSecondarySyncComplete(AbstractEffector *effector) {
  283.   // if moving a stepper after homing via the postAutoSyncMove int in isStepperAutoHomeComplete callback
  284.   // this callback is called after that additional move is complete
  285.  
  286.   // in this example, after doing the additional post limit switch steps
  287.   // if the effector has an identifer of "6", set pin 12 high
  288.  
  289.   // char effectorIdentifier[9];
  290.   // effector->getIdentifier(effectorIdentifier, 9);
  291.  
  292.   // if (strcmp(effectorIdentifier, "6") == 0)
  293.   // {
  294.   //      digitalWrite(12, HIGH);
  295.   // }
  296. }
  297. }  // namespace Callbacks
Advertisement
Add Comment
Please, Sign In to add comment