I was so excited to share my final result that I very nearly forgot to post the code. Here it is below. I have to admit that significant portions of it were dictated or outright created by my Hunky Assistant. But the challenge did prompt me to do a couple of smaller projects better suited to a programming novice. Anyway, take a look! Let me know if there's a better way to do what we did...
#include <TinyWireM.h> //software I2C library, lets you talk to any I2C device
#include <Adafruit_NeoPixel.h>
#include <util/delay.h>
#define NEOPIXEL_PIN 1
// this is the "callsign" of the accelerometer.
// When it hears this, it knows the Gemma is talking to him
#define ACCEL_ADDR (0x32 >> 1)
// this is the address for the output of one byte for the X low order bits.
// The y and z registers immediately follow this so we can just have the
// computer call this one and the 5 that follow
#define OUT_X_L_A 0x28
#define CTRL_REG1_A 0x20
#define NUM_PIXELS 4
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_PIXELS, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);
const uint8_t stages[][3] = {
{ 0, 0, 0 },
{ 0, 0, 60 },
{ 0, 60, 0 },
{ 127, 0, 0 },
{ 255, 0, 255 },
{ 0, 255, 255 },
{ 255, 255, 0 },
{ 255, 255, 255 },
};
const size_t num_stages = sizeof(stages)/sizeof(stages[0]);
void init_accelerometer()
{
TinyWireM.beginTransmission(ACCEL_ADDR); // start sending a message to this address: ACCEL_ADDR
TinyWireM.send(CTRL_REG1_A); // send this byte as part of the message: CTR:L_REG1_A
TinyWireM.send(0x37); // send this other byte as part of the message: 0x37
TinyWireM.endTransmission(); // finish sending the message
//the sum of these functions sets the mode of the accelerometer
}
void setup()
{
TinyWireM.begin(); // this initializes the ability to talk over I2C
init_accelerometer(); // this initializes the ability to talk with the accelerometer
strip.begin(); //this initializes the "strip" aka the NeoPixels
strip.show(); //initializes all pixels off
}
int16_t recv_2bytes()
{
uint16_t v;
uint8_t l = TinyWireM.receive();
uint8_t h = TinyWireM.receive();
v = h;
v <<= 8;
v |= l;
return v;
//this part gets the values of x y z and reassembles the two bytes of each into one number
}
void get_accelerometer_values(int16_t *x, int16_t *y, int16_t *z) // asterisks mean to get the address, not value
{
TinyWireM.beginTransmission(ACCEL_ADDR); // begin transmission to a device that has this address
TinyWireM.send(OUT_X_L_A | 0x80); // part of the transmission is this
TinyWireM.endTransmission(); // finish transmission
TinyWireM.requestFrom(ACCEL_ADDR, 6); // start getting information from the accelerometer
*x = recv_2bytes();
*y = recv_2bytes();
*z = recv_2bytes();
}
#define iter1(N) \
tr = root + (1 << (N)); \
if (n >= tr << (N)) \
{ n -= tr << (N); \
root |= 2 << (N); \
}
uint32_t sqrti(uint32_t n)
{
uint32_t root = 0, tr;
int N;
for (N=15; N>=0; N--) {
tr = root + (1 << (N));
if (n >= tr << (N)) {
n -= tr << (N);
root |= 2 << (N);
}
}
return root >> 1;
}
uint16_t get_accelerometer_vector_length()
{
int16_t x,y,z; // make a space for an integer of 16 bits, a space each for x,y,z
int32_t t;
int32_t v;
get_accelerometer_values(&x, &y, &z);
//this is a little complicated. "&" has a very specific meaning
//Give me the addresses of where "x" "y" "z" live in memory
v = 0.0;
t = x;
t *= t; // t=t*t
v += t; // v=v+t
t = y;
t *= t;
v += t;
t = z;
t *= t;
v += t;
v = sqrti(v);
// here we're taking the values of x y z and squaring them and adding that and then getting the square root of the whole
v -= 16384.0;
if (v < 0) {
v = -v; // inverting the number. taking a negative and making it positive -- getting the absolute value
}
return v; // return is a very special keyword in C
//it is a statement, not a variable declaration
//it means "stop executing this function and return a value"
}
void out_value(uint8_t v)
{
uint8_t i;
for (i = 0; i < 8; i++) {
digitalWrite(1, LOW);
delay(1);
digitalWrite(1, HIGH);
delay(1);
digitalWrite(1, LOW);
delay(1);
digitalWrite(1, (v>>(7-i))&1);
delay(4);
}
digitalWrite(1, LOW);
delay(100);
}
void interpolate_detailed(int frame, const uint8_t *initial, const uint8_t *next, uint8_t *vals)
{
int i;
for (i = 0; i < 3; i++) {
int16_t attrib_prev_val;
int16_t attrib_next_val;
attrib_prev_val = initial[i];
attrib_next_val = next[i];
int32_t tmp = (attrib_next_val - attrib_prev_val);
tmp = tmp * frame / 255;
vals[i] = attrib_prev_val + tmp;
}
}
void apply_fade(uint16_t current_fade_frame, uint16_t total_fade_frames, uint8_t *values)
{
int i;
for (i=0; i<3; i++) {
uint32_t tmp = (total_fade_frames - current_fade_frame);
tmp *=values[i];
tmp /=total_fade_frames;
values[i] = tmp;
}
}
#define ACC_THRESHOLD 1000
enum {
STATE_ZERO,
STATE_INCREASING,
STATE_WAITING,
STATE_DECREASING,
} mstate = STATE_ZERO;
/* Shared */
uint8_t current_values[3] = {0, 0, 0};
int current_stage = 0;
/* Increasing */
//uint8_t goal[3];
int goal_stage;
int current_frame;
/* Waiting */
int iterations_left;
/* Decreasing */
// use current_frame too
uint16_t fade_frames;
uint16_t current_fade_frame;
void loop()
{
int16_t acc_sample;
acc_sample = get_accelerometer_vector_length(); // first, call the function get_accelerometer_vector_length and put its value in a
int i;
int delay_val = 0;
if (mstate == STATE_ZERO) {
current_stage = 0;
delay_val = 10;
if (acc_sample > ACC_THRESHOLD) {
/* Transition to increasing */
goal_stage = current_stage + 1;
current_frame = 0;
delay_val = 0;
mstate = STATE_INCREASING;
}
} else if (mstate == STATE_INCREASING) {
interpolate_detailed(current_frame, stages[goal_stage-1], stages[goal_stage], current_values);
if (current_frame == 255) {
/* Transition to waiting */
iterations_left = 100;
current_stage++;
mstate = STATE_WAITING;
}
current_frame++;
delay_val = 10;
} else if (mstate == STATE_WAITING) {
iterations_left--;
if (acc_sample > ACC_THRESHOLD && current_stage < num_stages - 1) {
/* Transition to increasing */
goal_stage = current_stage + 1;
current_frame = 0;
delay_val = 0;
mstate = STATE_INCREASING;
}
delay_val = 10;
if (iterations_left == 0) {
/* Transition to decreasing */
current_frame = 0;
current_fade_frame = 0;
fade_frames = current_stage * 256;
mstate = STATE_DECREASING;
}
} else if (mstate == STATE_DECREASING) {
const uint8_t black[3] = { 0, 0, 0};
interpolate_detailed(current_frame, stages[current_stage], stages[current_stage-1], current_values);
apply_fade(current_fade_frame, fade_frames, current_values);
current_frame++;
delay_val = 10;
current_fade_frame++;
if (current_frame == 255) {
current_stage--;
if (current_stage == 0) {
/* Transition to zero */
mstate = STATE_ZERO;
} else {
current_frame = 0;
}
}
}
for (i=0; i<NUM_PIXELS; i++) {
strip.setPixelColor(
i,
current_values[0],
current_values[1],
current_values[2]);
}
strip.show();
delay(delay_val);
}
