Now what if you're out riding around and get tired of the current animation and want to change it. Ride all the way home and reload a different animation? Nah. Let's see how we can have them all loaded and then choose the one we want using the buttons on the Circuit Playground.

Doing Two (or more) Things At Once

As simple as the idea of changing animations with a button press is, it has some surprising complexities. At it's lowest level, the brain on the Circuit Playground can only do one thing at a time. So if it's busy turning NeoPixels on and off, how can it check on button presses?

There are various approaches. For our bike light, we will use the simplest of them. However, for an excellent overview of more complex approaches (including using interrupts), check out this three part series. The ideas discussed in these guides could be used on the Circuit Playground.

What we will do is take advantage of the fact that all of our animation loops run fairly fast. This makes it possible to simply check for any button presses at the beginning (or end) of the animation loop. Then, if a button is pressed, take some action - like move to the next animation. This isn't perfect and has some issues, as you'll see.

To do this, we first take all of the animation code from our previous sketches and move them into functions that have the form:

void animation1() {
  while (!buttonsPressed()) {
    // animation code goes here  
  }
}

We'll create a function for each animation. The while() loop will drive the animation and will continue as long as no buttons are pressed.

Then, our main Arduino loop() will look something like this:

void loop() {
  animation1(); delay(250);
  animation2(); delay(250);
  animation3(); delay(250);
  // etc
}

So the first animation is launched with a call to animation1(). That will run until a button is pressed. When a button is pressed, the animation function exits and the next animation function animation2() is called. Which runs until a button is pressed, etc.

The delay() is needed so that you don't cycle through the animations super fast when you press a button. This is a simple form of debounce.

Here's the final code:

///////////////////////////////////////////////////////////////////////////////
// Circuit Playground Bike Light - The All Of Them
//
// Author: Carter Nelson
// MIT License (https://opensource.org/licenses/MIT)

#include <Adafruit_CircuitPlayground.h>

// Change these to set speed (lower is faster)
#define FLASH_RATE    250
#define SPIN_RATE     100
#define CYLON_RATE    100
#define BEDAZZLE_RATE 100
#define CHASE_RATE    100

// Change these to be whatever color you want
// Use color picker to come up with hex values
#define FLASH_COLOR   0xFF0000
#define SPIN_COLOR    0xFF0000
#define CYLON_COLOR   0xFF0000

// Define 10 colors here.
// Must be 10 entries.
// Use 0x000000 if you want a blank space.
uint32_t rainbowColors[] = {
  0xFF0000,   
  0xFF5500,
  0xFFFF00,
  0x00FF00,
  0x0000FF,
  0xFF00FF,
  0x000000,
  0x000000,
  0x000000,
  0x000000
};

///////////////////////////////////////////////////////////////////////////////
bool buttonsPressed() {
  return CircuitPlayground.leftButton() | CircuitPlayground.rightButton();  
}

///////////////////////////////////////////////////////////////////////////////
void flasher() {
  while (!buttonsPressed()) {
    // Turn on all the pixels to FLASH_COLOR
    for (int pixel=0; pixel<10; pixel++) {
      CircuitPlayground.setPixelColor(pixel, FLASH_COLOR);    
    }
    
    // Leave them on for a little bit  
    delay(FLASH_RATE);
  
    // Turn off all the NeoPixels
    CircuitPlayground.clearPixels();
  
    // Leave them off for a little bit
    delay(FLASH_RATE);
  }
}

///////////////////////////////////////////////////////////////////////////////
void spinner() {
  // Can be any two pixels
  int pixel1 = 0;
  int pixel2 = 5;
    
  while (!buttonsPressed()) {
    // Turn off all the NeoPixels
    CircuitPlayground.clearPixels();
  
    // Turn on two pixels to SPIN_COLOR
    CircuitPlayground.setPixelColor(pixel1, SPIN_COLOR);
    CircuitPlayground.setPixelColor(pixel2, SPIN_COLOR);
  
    // Increment pixels to move them around the board
    pixel1 = pixel1 + 1;
    pixel2 = pixel2 + 1;
  
    // Check pixel values
    if (pixel1 > 9) pixel1 = 0;
    if (pixel2 > 9) pixel2 = 0;
  
    // Wait a little bit so we don't spin too fast
    delay(SPIN_RATE);
  }
}

///////////////////////////////////////////////////////////////////////////////
void cylon() {
  int pixel1 = 0;
  int pixel2 = 9;
  
  while (!buttonsPressed()) {
    // Scan in one direction
    for (int step=0; step<4; step++) {
      CircuitPlayground.clearPixels();
    
      CircuitPlayground.setPixelColor(pixel1, CYLON_COLOR);
      CircuitPlayground.setPixelColor(pixel2, CYLON_COLOR);
  
      pixel1 = pixel1 + 1;
      pixel2 = pixel2 - 1;
      
      delay(CYLON_RATE);    
    }
  
    // Scan back the other direction
    for (int step=0; step<4; step++) {
      CircuitPlayground.clearPixels();
    
      CircuitPlayground.setPixelColor(pixel1, CYLON_COLOR);
      CircuitPlayground.setPixelColor(pixel2, CYLON_COLOR);
  
      pixel1 = pixel1 - 1;
      pixel2 = pixel2 + 1;
      
      delay(CYLON_RATE);    
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
void bedazzler() {
  while (!buttonsPressed()) {
    // Turn off all the NeoPixels
    CircuitPlayground.clearPixels();
  
    // Turn on a random pixel to a random color
    CircuitPlayground.setPixelColor(
      random(10),     // the pixel
      random(256),    // red  
      random(256),    // green
      random(256) );  // blue  
  
    // Leave it on for a little bit
    delay(BEDAZZLE_RATE);
  }
}

///////////////////////////////////////////////////////////////////////////////
void rainbow() {
  // Start at the beginning
  int startIndex = 0;
  int colorIndex;

  while (!buttonsPressed()) {
    // Turn off all the NeoPixels
    CircuitPlayground.clearPixels();
  
    // Loop through and set pixels
    colorIndex = startIndex;
    for (int pixel=0; pixel<10; pixel++) {
      CircuitPlayground.setPixelColor(pixel, rainbowColors[colorIndex]);
      colorIndex++;
      if (colorIndex > 9) colorIndex = 0;
    }
  
    // Increment start index into color array
    startIndex++;
  
    // Check value and reset if necessary
    if (startIndex > 9) startIndex = 0;
  
    // Wait a little bit so we don't spin too fast
    delay(CHASE_RATE);
  }
}

///////////////////////////////////////////////////////////////////////////////
void setup() {
  CircuitPlayground.begin();
  
  // Make it bright!
  CircuitPlayground.setBrightness(255);
}

///////////////////////////////////////////////////////////////////////////////
void loop() {
  flasher();    delay(250);
  spinner();    delay(250);
  cylon();      delay(250);
  bedazzler();  delay(250);
  rainbow();    delay(250);
  // TODO: add your animation here!
}

Issues

With the above code loaded and running on the Circuit Playground, try changing the animations by pressing either of the two buttons. Did it work? If not try pressing it again. Work that time?

So you might notice that the button response is a little sluggish. Sometimes it will change, sometimes it won't. This is the main issue with the simple approach we've taken for checking button presses. The responsiveness is coupled to the speed of the animation. You could make the problem even worse by slowing down an animation. Then the button is checked even less frequently and becomes even more sluggish.

Kind of annoying, isn't it? If you're motivated to try and make it better, read the guides linked above.

This guide was first published on Apr 24, 2017. It was last updated on Apr 24, 2017.

This page (The All Of Them) was last updated on Oct 23, 2021.

Text editor powered by tinymce.