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); }