:: Using the ADXL330 Three-Axis Accelerometer ::

We make a "sleeping" Arduino, then we annoy it by shaking.

If left alone, make a light pummer "snore" in peaceful colors. If agitated by shaking or dropping the device, breath faster in hotter colors. (Take care not to damage the poor fellow by dropping it onto a hard floor!)

A basic example sketch that reads from analog three-pin three-axis accelerometers, such as the common ADXL330 chip found at Seeedstudio SparkFun online vendors.

In addition, this sketch outputs PWM signals to three LEDs, such as a self-contained RGB LED package or three independently wired LEDs, one each of red, green and blue suggested.

Optionally, wiring a switch from digital pin 8 to ground will give this sketch a chance to "calibrate" the accelerometer. Not required but useful, since every accelerometer varies slightly.

/*
 * Sleeping Arduino - unless you jostle it!
 *
 * If left alone, make a light pummer "snore" in peaceful colors.  If
 * agitated by shaking the device, breath faster in hotter colors.
 *
 * A basic example sketch that reads from analog three-pin three-axis
 * accelerometers, such as the common ADXL330 chip found at Seeedstudio
 * or SparkFun online vendors.
 *
 * In addition, this sketch outputs PWM signals to three LEDs, such as
 * a self-contained RGB LED package or three independently wired LEDs,
 * one each of red, green and blue suggested.
 *
 * Optionally, wiring a switch from digital pin 8 to ground will give
 * this sketch a chance to "calibrate" the accelerometer.  Not required
 * but useful, since every accelerometer varies slightly.
 *
 * Released (cc) Creative Commons Attribution Only
 * Ed Halley
 * [ e d @ h a l l e y . c c ]
 *
 */


#include <math.h>

/* Pummer:
 * A simple RGB color-interpolating helper class.
 *
 * When creating one, tell it which three output pins to drive PWM signals.
 * If your RGB device is common-anode, it can reverse the PWM for you.
 * Don't forget to limit current to each LED with a resistor (e.g., 220ohm).
 *
 * At any time, tell it what color to become by calling the goal() method,
 * and how fast to transition to that color.
 *
 * Call the pummer's loop() method occasionally to let it set the PWM
 * outputs to the LEDs.
 */

class Pummer
{
    byte lR, lG, lB;
    byte nR, nG, nB;
    byte wR, wG, wB;
    int pR, pG, pB;
    unsigned long last, when;
    boolean reverse;

public:
    Pummer(int pinR, int pinG, int pinB, boolean anode=false)
    {
        pinMode(pR = pinR, OUTPUT);
        pinMode(pG = pinG, OUTPUT);
        pinMode(pB = pinB, OUTPUT);
        nR = nG = nB = 0;
        reverse = anode;
        show();
        goal(255, 255, 255);
    }

    void show()
    {
        analogWrite(pR, reverse? (255-nR) : nR);
        analogWrite(pG, reverse? (255-nG) : nG);
        analogWrite(pB, reverse? (255-nB) : nB);
    }

    boolean done() { return last == when; }

    void goal(byte r, byte g, byte b, unsigned long speed = 500)
    {
        lR = nR; lG = nG; lB = nB;
        wR = r; wG = g; wB = b;
        last = millis();
        when = last + speed;
    }

    void loop()
    {
        unsigned long now = millis();
        if (now > when)
        {
            if (last == when)
                return;
            nR = wR; nG = wG; nB = wB;
            last = when;
        }
        else
        {
            nR = map(now, last, when, lR, wR);
            nG = map(now, last, when, lG, wG);
            nB = map(now, last, when, lB, wB);
        }
        show();
    }
};

/* Accelerometer:
 * Receive input from a 3-axis device, and perform some useful calculations.
 *
 * Specify the three axis pins, and the VCC and GND pins, using analog pin
 * numbers.  These are usually adjacent on the common breakout boards.
 *
 * Call the accelerometer's update() method occasionally to update the
 * current values from the hardware.
 *
 * Get the axis acceleration features with the axis() method, or other useful
 * calculations such as milligee(), pitch() and roll().
 *
 * Note that some functions like pitch() and roll() use floating point math,
 * and can therefore really increase the size of the program on the chip.
 * You can save about 2000 bytes if you don't need any floating point math.
 */

#define ANALOG0 14

class Accelerometer
{
    int p[3]; // which analog pins
    int a[3]; // acceleration, zero-based
    int b[3]; // acceleration bias/calibration information
    int g, t, r; // cached copies of calculations
    int scale; // scaling factor between ADC and gravity

public:
    Accelerometer(int pinX, int pinY, int pinZ, int pinVCC, int pinGND)
    {
        pinMode(pinGND + ANALOG0, OUTPUT); digitalWrite(pinGND + ANALOG0, LOW);
        pinMode(pinVCC + ANALOG0, OUTPUT); digitalWrite(pinVCC + ANALOG0, HIGH);
        pinMode((p[0] = pinX) + ANALOG0, INPUT);
        pinMode((p[1] = pinY) + ANALOG0, INPUT);
        pinMode((p[2] = pinZ) + ANALOG0, INPUT);
        for (int i = 0; i < 3; i++)
            b[i] = 512;
        g = t = r = 0;
        scale = 100;
    }

    void update()
    {
        for (int i = 0; i < 3; i++)
            a[i] = analogRead(p[i]) - b[i];
        g = t = r = 0;
    }

    void dump()
    {
        Serial.print(  "x="); Serial.print(a[0]);
        Serial.print("\ty="); Serial.print(a[1]);
        Serial.print("\tz="); Serial.print(a[2]);
        Serial.print("\tmg="); Serial.print(milligee());
        Serial.print("\tpitch="); Serial.print(pitch());
        Serial.print("\troll="); Serial.print(roll());
        Serial.println();
    }

    void calibrate()
    {
        for (int i = 0; i < 3; i++)
            b[i] = analogRead(p[i]);
        b[2] -= scale;
        update();
    }

    int milligee()
    {
        if (g != 0) return g;
        long squared = 0.0;
        for (int i = 0; i < 3; i++)
            squared += (long)a[i] * (long)a[i];
        g = squared * 1000 / (scale*scale);
        return g;
    }

    int accel(int axis)
    {
        if (axis < 0 || axis > 3) return 0;
        return a[axis];
    }

    int roll()
    {
        if (r != 0) return r;
        r = (int)(atan2(a[0], a[2]) * 180. / M_PI);
        return r;
    }

    int pitch()
    {
        if (t != 0) return t;
        t = (int)(acos(a[1] / (float)scale) * 180. / M_PI);
        return t;
    }

    void loop()
    {
        update();
    }
};

/*
 * Example Program
 *
 * Sleeping Arduino - unless you jostle it!
 *
 * If you wire a button from ground to pin 8, you can press it
 * once when the device is at rest on a level table, to get
 * somewhat better calibration data.  We do not try to save the
 * calibration into EEPROM, but that would be handy for more
 * permanent uses of an accelerometer.
 *
 */


void loop() { ; } // we do our own loop below

void setup()
{
    Serial.begin(9600);

    boolean grow = true;
    int anger = 0;
    Pummer pummer = Pummer(5, 6, 3, true);
    Accelerometer accel = Accelerometer(3, 2, 1, 5, 4);

    pinMode(8, INPUT); digitalWrite(8, HIGH); // internal pullup

    int div = 0;
    while (1)
    {
        delay(20);
        accel.loop();
        if (LOW == digitalRead(8))
            accel.calibrate();
        if (--div <= 0) { div = 8; accel.dump(); } // print every 8th loop

        // get more and more annoyed at high gee forces,
        // get instantly annoyed at zero gee force (freefall),
        // fall back asleep at normal gee forces
        //
        if (accel.milligee() > 1800)
            anger += 5;
        else if (accel.milligee() < 200)
            anger = 255;
        else if (accel.milligee() < 1100)
            anger -= 1;
        anger = max(0, min(anger, 255));

        // show the state of mind with our pummer
        pummer.loop();
        if (pummer.done())
        {
            grow = !grow;
            if (grow)
                pummer.goal(anger, 64, 64-anger/4, 350-anger);
            else
                pummer.goal(anger/2, anger/4, 128-anger/2, 2000-anger*7);
        }
    }
}

Share