Wednesday, August 24, 2016

An Adafruit Circuit Playground Rolling in a Pringles Can

Good response today as I posted on social media the picture below - the Adafruit Circuit Playground low cost experimenter board and a 3xAAA battery pack fit well in a Pringles potato chip (crisp) can:


So I have been playing with the accelerometer sensor in the Circuit Playground. You can do some sophisticated things with an accelerometer.  Let's see what I did with the can.

Parts:
Adafruit Circuit Playground experimenters board - $19.95
3xAAA Battery Pack - $1.95
3 AAA batteries
Pringles Can - from the bin
Putty - from the craft supplies basket

Use some Blu-Tack, silly putty, clay, etc. to attach Circuit Playground to the battery pack. Just be sure the adhesive is not permanent and non-conductive. Plug the battery pack into the Circuit Playground and turn it on. Slide it into the can, it'll fit fine and not slide around (yay!).



First, I'll light up one Circuit Playground LED NeoPixel green. Program Circuit Playground with the first sketch below (which turns on NeoPixel #2).  Then I put the Circuit Playground into the can so it is fixed to the bottom. Then I roll the can. The LED will do loop-de-loops in circles similar to the following diagram:

And in the dark it looks like this:

Now, say I want the LED to always be at the top of the can. I can try to time the lighting of the LED to the rate at which I spin the can but I'm not a good can roller and it would get all messy.

So I use the accelerometer on Circuit Playground to tell which way is "up" as the can rolls at any speed.  An accelerometer measures both the pull of gravity on the sensor and changes in movement. If you roll Circuit Playground like we did above, there will be changes in the X and Y directions (the Z axis doesn't change rolling the can as I have mounted it).  See the board for which ways are

Is keeping the LED lit at the top of the ring as the can rolls even possible?  Yes. It takes some math though. You can read the acceleration values as the can rolls in rolling distance and as the can presses on the ground due to gravity. Kind of like the figure below from the Wikipedia article Circular Motion under the non-uniform heading
Program the Circuit Playground with the second sketch below. The code boils down to finding the angle between the x and y values of the accelerometer reading, calculating an angle. That angle is used to determine which of the 10 NeoPixels on Circuit Playground needs to be lit to remain at the top of the ring. That method allows for the rate of rolling the can (shown as v in the diagram) to vary and the rate of rolling does not matter (except a tiny bit for code speed). 

The LED at the top of the circle is lit at all times. It looks like this in the Pringles can:


So with little more than an empty can, Circuit Playground and a battery pack, we've used a bit of sensing and programming to make something do what intuitively it shouldn't do. This behavior is actually VERY useful and can be used to make bigger and better things.

And that's the beauty of Circuit Playground.  You can do so much with the capabilities onboard and you don't need to buy a lot of stuff to make it do great things, just look in the cupboard perhaps.

Code


Code for one LED:

// CPoneLED
//
// Set for Circuit Playground to display one LED Continually
//
// Mike Barela  August 23, 2016  MIT License

#include <Adafruit_CircuitPlayground.h>

#define brightness 16

void setup()
{
  CircuitPlayground.begin();
  CircuitPlayground.setBrightness(brightness);
}

void loop()
{
  if(CircuitPlayground.slideSwitch()) {
    CircuitPlayground.setPixelColor(2,0,brightness,0);
  }
  else {
    CircuitPlayground.setPixelColor(2,0,0,0);
  }
  CircuitPlayground.strip.show();
}

and code for the accelerometer aided LED:

// CPaccelerometerLED 
//
// Set the LED at the "top" of the Circuit Playground NeoPixel ring
//   no matter the x or y orientation (like when rolling)
//
// Mike Barela  August 23, 2016  MIT License

#include <Adafruit_CircuitPlayground.h>

#define NUMBER_OF_LEDS_ON_RING 10
const int brightness = 16;
int ledPosition, currentQueueSize;

void setup() {
  CircuitPlayground.begin();
  CircuitPlayground.setBrightness(brightness);
  CircuitPlayground.clearPixels();
}

int led, previousLed=0;
float x, y, nx, ny, angle; 

void loop(){

  x = CircuitPlayground.motionX();  // Read the accelerometer X & Y
  y = CircuitPlayground.motionY();
  nx = x / 10.0;                    // Scale to -1 to +1 in
  ny = y / 10.0;                    //    both directions
  angle = atan((ny/nx)) * 180 / 3.14; // get the angle from "down"

  if(angle > 0.0){   // As Arctangent won't give the angle over the
    if(nx < 0.0)     // entire 360 degrees, adjust depending
      angle += 180;  // on direction the acceleration was going
  } 
  else {
    if(ny > 0.0)
      angle += 180;
    else
      angle += 360;
  }

  if(angle == 360.0)
    angle = 0;

  // We have 10 LEDs in a circle - need to light the right one
  led = circularize(angle / (360 / NUMBER_OF_LEDS_ON_RING));

  // make led movement smooth
  if(previousLed == led){  // no movement, just reloop
    // nothing to do 
  }
  else if (counterClockwiseDistanceBetweenLeds(previousLed, led) <= 8) {
    led = circularize(previousLed + 1); // change detected, 
    makeLightShow(previousLed, led);    // change LED
    previousLed = led;
  }
  else {
    led = circularize(previousLed - 1);
    makeLightShow(previousLed, led);
    previousLed = led;
  }

  delay(25);
}

void makeLightShow(int previousLed, int led) { // light NeoPixel
  CircuitPlayground.strip.setPixelColor(previousLed,0,0,0);
  CircuitPlayground.strip.setPixelColor(led, 0, brightness, 0);
  CircuitPlayground.strip.show();
}

int circularize(int pos){ // if a position gets to be < 0 or > 9
  if(pos >= NUMBER_OF_LEDS_ON_RING)
    return(pos - NUMBER_OF_LEDS_ON_RING);
  else if(pos < 0)
    return(pos + NUMBER_OF_LEDS_ON_RING);
  else
    return(pos);
}

int counterClockwiseDistanceBetweenLeds(int prevPos, int nextPos){
  int distance;
  distance = nextPos - prevPos;
  if(distance < 0)
    distance += NUMBER_OF_LEDS_ON_RING;
    
  return(distance); 
}

An exercise for the student / magician is to make one sketch, it acts one way with the slide switch at the + position, another when switched to -.

Code heavily modified from https://petervojtek.github.io/diy/2015/01/24/neopixel-gravitation.html, the page gives some good info on the subject also.