Before You Start

If this is your first foray into the world of arduino-based microcontrollers, you'll need to install some software first.  Head over to the Circuit Playground Lesson 0 guide for detailed installation and setup instructions.  

FastLED Library

You will also need to install the FastLED library in Arduino (Sketch > Include Library > Manage Libraries...)

One other note:  if you're using FastLED with Circuit Playground, be sure to #include the Circuit Playground library FIRST and theFastLED library second, or you may run into problems.

Upload Code

Once you've got everything installed and your computer can talk to the Circuit Playground, it's time to upload the code.

Plug your Circuit Playground into your computer and select the Circuit Plaground under Tools > Boards.  Then select the Circuit Playground as the Port.

Copy and paste this code (by Phil Burgess) into a new Arduino window and click "upload".

// "Amazon bracelets" sketch for Circuit Playground and NeoPixels.
// Press the RIGHT button on Circuit Playground for AUTO mode -- LEDs
// are then ON most of the time, best for photographing one's get-up.
// Press the LEFT button for DEFLECT mode -- LEDs are off until a loud
// sound or fast movement are detected.  Pew pew!  Deflect bullets!
// Triumph not with fists or firepower, but with love!

#include <Adafruit_CircuitPlayground.h>
#include <FastLED.h>

#define CP_PIN           17 // Circuit Playground's NeoPixels are on pin 17
#define CP_LEN           10 // Number of NeoPixels on Circuit Playground
#define STRIP_PIN         6 // NeoPixel strip is connected to this pin
#define STRIP_LEN        13 // Length of NeoPixel strip
#define SINGLES_PIN      12 // Single NeoPixels are connected to this pin
#define SINGLES_LEN       2 // Number of NeoPixel singles in chain
#define COLOR_ORDER     GRB
#define FPS              80 // Animation frames-per-second
#define TRIGGER_TIME 250000 // Debounce time after sound/motion trigger (microseconds)

// SOUND REACTIVE SETUP --------------
// Higher number = less sensitive (louder sound required to trigger)
#define SOUND_THRESHOLD 200 // Range 0 to 341


// MOTION REACTIVE SETUP --------------
// Higher number = less sensitive (faster acceleration required to trigger)
#define G_THRESHOLD 3.0 // Range 1.0 to 8.0 G's

int deflectionping = 1; // Bullet deflection "ping" sound.  Change to 0 to turn this off

// LED animation patterns cycle among several states.  There are two main modes
// (selected with the two buttons on Circuit Playground), each with some sub-states.
// In the 'auto' mode, the animation happens on its own, not in response to sound
// or motion.  In this mode it alternates between two states -- an 'idle' state
// with a solid color, and a periodic random flicker to add interest.  Best for
// photography, as it's all lit up most of the time.  In the 'deflect' mode,
// animation happens in response to sound and motion -- certain stimuli trigger a
// series of states for a 'deflected bullet' animation.  These goes from idle
// (awaiting sound/motion), flicker (the initial animation upon a 'bullet hit'),
// solid (brief state after hit) and back to idle (during which any lit LEDs will
// slowly fade), unless another bullet is deflected.
// An 'enumeration' is just the thing for this -- it's a C feature that creates a
// list of sequential integer constants by name, and ensures any variables of this
// type are not assigned outside the list.
enum {
  AUTO_IDLE,           // Auto mode (no sound/motion reaction) idle appearance
  AUTO_FLICKER,        // Auto mode, periodic random flicker
  DEFLECT_IDLE,        // Deflect mode (sound/motion reactive) idle appearance
  DEFLECT_FLICKER,     // Deflect mode, short flicker upon deflecting a bullet
  DEFLECT_SOLID,       // Deflect mode, short solid-color appearance
} mode = DEFLECT_IDLE; // Start out in DEFLECT_IDLE mode

// Color for solid() mode
#define HUE         24 // Orangey fire-ish color
#define SATURATION 255
#define BRIGHTNESS 255

CRGB cp[CP_LEN],               // Separate arrays for Circuit Playground pixels,
     strip[STRIP_LEN],         // strip and singles, so brightness can be
     singles[SINGLES_LEN];     // controlled separately if needed

uint32_t lastFrameTime   = 0L, // Used for frame-to-frame interval timing
         lastTriggerTime = 0L; // Used to 'debounce' sound & motion inputs
uint16_t frameCounter;         // Counter for animation timing

void setup() {
  CircuitPlayground.begin();
  FastLED.addLeds<WS2812B, CP_PIN     , COLOR_ORDER>(cp     , CP_LEN     ).setCorrection(TypicalLEDStrip);
  FastLED.addLeds<WS2812B, STRIP_PIN  , COLOR_ORDER>(strip  , STRIP_LEN  ).setCorrection(TypicalLEDStrip);
  FastLED.addLeds<WS2812B, SINGLES_PIN, COLOR_ORDER>(cp, SINGLES_LEN).setCorrection(TypicalLEDStrip);  //singles are set up with the cp array so they act like the Circuit Playground onboard LEDs
  set_max_power_in_volts_and_milliamps(5, 500); // FastLED 2.1 Power management set at 5V, 500mA
  fill_solid(cp     , CP_LEN     , CRGB::Black);
  fill_solid(strip  , STRIP_LEN  , CRGB::Black);
}

void loop() {
  uint32_t currentTime;

  // Frame-to-frame timing is kept semi-consistent-ish by checking the current
  // time against the prior-frame time.  Rather than burning off this time with
  // a delay() call, this is a perfect opportunity to test for any button,
  // sound or motion inputs...repeatedly, if need be, until the elapsed frame
  // interval is passed.  A do/while loop is used so these tests are always
  // performed at least once per frame (else inputs might be ignored if there's
  // heavy animation going on).
  do {
    currentTime = micros();

    // Check first for button presses...
    if(CircuitPlayground.leftButton()) {         // Left button pressed?
      mode            = DEFLECT_IDLE;            //   Yes, switch to motion/sound reactive mode
      lastTriggerTime = currentTime;             //   Note time of activation
      while(CircuitPlayground.leftButton());     //   Wait for button release
    } else if(CircuitPlayground.rightButton()) { // Right button pressed?
      mode            = AUTO_IDLE;               //   Yup, switch to 'auto' mode (not reactive)
      frameCounter    = FPS;                     //   AUTO_IDLE animation for 1 second
      lastTriggerTime = currentTime;             //   Note time of activation
      while(CircuitPlayground.rightButton());    //   Wait for button release
  
    // Else no button presses.  If we're in a 'deflect' mode,
    // and if sufficient time has passed since last trigger...
    } else if((mode >= DEFLECT_IDLE) &&
             ((currentTime - lastTriggerTime) >= TRIGGER_TIME)) {
      // ..then check for sound and motion...

      int value = CircuitPlayground.soundSensor() - (1023 / 3); // Audio amplitude
      if(abs(value) >= SOUND_THRESHOLD) {    // Loud noise?
          ping();
          mode            = DEFLECT_FLICKER; //   Bullet deflected!  Pew pew!
          frameCounter    = FPS * 3 / 2;     //   Flicker for 1.5 sec
          lastTriggerTime = currentTime;     //   Turn off detection for a moment
      } else {                               // No sound, check accelerometer...
        sensors_event_t event;
        CircuitPlayground.lis.getEvent(&event);
        // Compare magnitude of accelerometer reading against G_THRESHOLD.
        // Costly sqrt() is avoided by ^2 the threshold value for comparison.
        if(((event.acceleration.x * event.acceleration.x) +
            (event.acceleration.y * event.acceleration.y) +
            (event.acceleration.z * event.acceleration.z)) >=
           ((9.8 * G_THRESHOLD) * (9.8 * G_THRESHOLD))) {
          ping();
          mode            = DEFLECT_FLICKER; // Bullet deflected!  Pew pew!
          frameCounter    = FPS * 3 / 2;     // Flicker for 1.5 sec
          lastTriggerTime = currentTime;     // Turn off detection for a moment
        }
      }
    }
  // Repeat until frame interval has elapsed:
  } while((currentTime - lastFrameTime) < (1000000L / FPS));
  lastFrameTime = currentTime; // Save time for next frame

  // Done with button/sound/motion sensing.  LEDs are updated immediately after
  // interval test, for more uniform timing.  So this actually displays the colors
  // calculated on the *prior* loop() pass...
  FastLED.show();
  frameCounter--; // Count down to zero (cycles modes)
  // Then we render one frame of animation for the *next* pass...

  switch(mode) {

    // The first two modes -- AUTO_IDLE and AUTO_FLICKER -- are used when
    // sound/motion reactivity has been turned off.  The code will alternate
    // between these two states until reactivity is switched on (left button).
    case AUTO_IDLE:
      solid();                    // Show solid color
      if(!frameCounter) {         // for desired number of frames, then...
        mode = AUTO_FLICKER;      //   Switch to AUTO_FLICKER mode and
        frameCounter = FPS * 2;   //   set it to flicker for 2 seconds
      }
      break;
    case AUTO_FLICKER:
      flicker();                  // Show occasional random flicker
      if(!frameCounter) {         // for desired number of frames, then...
        mode         = AUTO_IDLE; //   Switch back to AUTO_IDLE mode
        frameCounter = random(3 * FPS, 10 * FPS); // for 3 to 10 sec.
      }
      break;

    // The remaining modes are used when 'deflecting bullets.'
    case DEFLECT_IDLE:                     // Awaiting action
      fade();                              // Dim LEDs slightly
      break;
    case DEFLECT_FLICKER:                  // Initial reaction to bullet hit
      flicker();                           // Random-ish LED flickering
      if(!frameCounter) {                  // FLICKER time elapsed?
        mode         = DEFLECT_SOLID;      //   Switch to DEFLECT_SOLID mode
        frameCounter = FPS / 2;            //   for next 1/2 sec
      }
      break;
    case DEFLECT_SOLID:                    // Short 'solid' period follows flicker
      solid();                             // Solid color
      if(!frameCounter) {                  // SOLID time elapsed?
        mode         = DEFLECT_IDLE;       //   Switch back to IDLE mode
      }
      break;
  }
}

void fade() {
  fadeToBlackBy(cp     , CP_LEN     , 85); // Fade Circuit Playground LEDs by 1/3
  fadeToBlackBy(strip  , STRIP_LEN  , 8);  // Fade other LEDs slightly
}

void flicker() {
  
  fadeToBlackBy(cp, CP_LEN, 85); // Fade Circuit Playground LEDs by 1/3
  if(!random(8)) {               // Then, if random 1/8 chance...
    // Set one random pixel on Circuit Playground to white
    cp[random(0, CP_LEN)] = CRGB::White;
  }
  if(!random(8)) { // A separate 1/8 chance...
    // Set all other LEDs to a random brightness
    CRGB c = CHSV(HUE, SATURATION, random(32, BRIGHTNESS));
    fill_solid(strip  , STRIP_LEN  , c);   
  } else {
    // Fade others slightly
    fadeToBlackBy(strip  , STRIP_LEN  , 8);
  }
}

void solid() {
  fadeToBlackBy(cp, CP_LEN, 85); // Fade Circuit Playground LEDs by 1/3
  CRGB c = CHSV(HUE, SATURATION, BRIGHTNESS);
  fill_solid(strip  , STRIP_LEN  , c); // Set other LEDs to solid color
}

void ping() {
  if (deflectionping==1) {
    CircuitPlayground.playTone(random(2000,2200), 30); 
  }
}

Press the RIGHT button on the Circuit Playground for AUTO mode -- lights are on all the time, great for photo shoots and peace talks.  You'll see random flashes every few seconds on the Circuit Playground's onboard LEDs, with no effort on your part.

Press the LEFT button to enter DEFLECT mode. Make some noise and/or shake your Circuit Playground around a bit.  The motion and sound will trigger a bullet-blocking animation.  The Circuit Playground will make a "ping" sound and the lights will flash randomly, simulating the spark of a bullet strike.

Note: If this "ping" sound drives you crazy, it's easy to turn it off in the code.  Look for this line:

int deflectionping = 1; // Bullet deflection "ping" sound.  Change to 0 to turn this off

Change the 1 to a 0 to turn off the sound.

This guide was first published on May 19, 2017. It was last updated on May 19, 2017.

This page (Code) was last updated on Apr 12, 2017.

Text editor powered by tinymce.