     recipient[x]=donor[x];
   }
 }
+
+boolean redundantFlashCommand(const int col1[], const int col2[], int fTime) {
+  for (int x=0; x<3; x++) {
+    if (flashcol1[x]!=col1[x]) {return false;}
+    if (flashcol2[x]!=col2[x]) {return false;}
+  }
+  if (flashTimePerColor_ms != fTime) {return false;}
+  return true;
+}
+

}

void flash() {
  if (debugMode) { Serial.print("Flash:");}
  outputColor( flashFirstColor ? flashcol1 : flashcol2 );
  flashFirstColor = !flashFirstColor;
  if (debugMode) { Serial.print(red); Serial.print(green); Serial.println(blue); }
}

void timeoutAction() {
  if (debugMode) { Serial.println("Processing timeout."); }
  if (!recentActivity) {
    Serial.print("No recent activity for "); Serial.print(TIMEOUT); Serial.println(" seconds.");
    // maybe timeout and timeout behavior should be configurable over serial.
    // low priority, bc this will mostly be running before serial comms have
    // been established.
    gradientPulseSetup(OFF, WHITE, 2);
    gradientLoop = true;
  } 
}

void gradientPulseSetup (const int col1[3],const int col2[3], float timev) { // timev is now in seconds
  flashThread.disable();
  gradDelayPerTick = (float) timev*1000/numdivs;
  gradPercPerTick = (float) 1/numdivs;
  gradPerc = 0;
  if (debugMode) {

const int  BLUE[] = {0,   0,   255};
const int WHITE[] = {255, 255, 255};
const int BAUDRATE = 19200;
const int SERIAL_BUFFER_LENGTH = 32;  //leaves 15 characters for pulseTime
const int SERIAL_CYCLE_LENGTH_MS = 70;
const int TIMEOUT_S = 600;  //seconds of no activity before beginning default pattern
const boolean TIMEOUT_ENABLED = false; // timeout is buggy
const float numdivs = 60.0; //tune this to adjust gradient smoothness

// ---------- GLOBAL VARIABLES ----------
boolean debugMode = false;
char inputBuffer[SERIAL_BUFFER_LENGTH];
boolean recentActivity = false;

int red,green,blue;
int* rgb[4] = {&red, &green, &blue, 0};

int gradcol1[3], gradcol2[3];
float gradPerc, gradPercPerTick, gradDelayPerTick_ms;
boolean gradientLoop = false, gradientReversingMode = true;

int flashcol1[3], flashcol2[3];
int flashTimePerColor_ms;
boolean flashLoop = true, flashingFirstColor = false;

// -------------- THREADS ---------------
#include <TimedAction.h>
TimedAction   serialThread = TimedAction(0, SERIAL_CYCLE_LENGTH_MS, &processSerialIn);
TimedAction gradientThread = TimedAction(0,  &gradientStep); // initially disabled in setup()
TimedAction    flashThread = TimedAction(0, flashTimePerColor_ms, &flash); // initially disabled in setup()
TimedAction  timeoutThread = TimedAction(TIMEOUT_S * 1000, TIMEOUT_S * 1000,  &timeoutAction);

void setup() {
  if (!GROVE_DRIVER) {
    pinMode(R_PIN,OUTPUT);
    pinMode(G_PIN,OUTPUT);
    pinMode(B_PIN,OUTPUT);
    pinMode(EXTRA_GND,OUTPUT);
  }
  gradientThread.disable();
  flashThread.disable();
  if (!TIMEOUT_ENABLED) { timeoutThread.disable(); }
  Serial.setTimeout(SERIAL_CYCLE_LENGTH_MS/7); //should be substantially less than serialThread's execution window
  Serial.begin(BAUDRATE);
  quickCycle(150); // power-up notification
  gradientPulseSetup(BLUE,OFF,2);
  gradientLoop = true;
  gradientReversingMode = true;
}

void loop() {  
