Remote controlled crossfading ping pong ball

Now that fall semester is over and winter break has begun, I’m gonna start making more stuff! Building on to the color-crossfading ping pong ball, I decided to use the 38kHz infrared receiver module and keychain remote I bought awhile ago to make it remote-controlled. Each button on the remote corresponds to a color, except the power button which turns it off.

It’s a modified version of this RGB LED crossfader program combined with the sample code provided in the keychain remote item page.

#define RED_PIN 11
#define GRN_PIN 10
#define BLU_PIN 9

#define IR_PIN 17

/* The next two defines are only here because I'm too lazy 
   to wire up the IR receiver module to power and ground, so I 
   just stuck all three pins into the analog input pins and used 
   digitalWrite() to set one to high and one to low */
#define IR_GROUND 18
#define IR_POWER 19

#define START_BIT 2200 //Start bit threshold (Microseconds)
#define BIN0_THRES 400 //Binary 0 threshold (Microseconds)
#define BIN1_THRES 1000 //Binary 1 threshold (Microseconds)

boolean commonAnode = false;

// Color arrays
int black[3]  = {0,0,0};
int white[3]  = {255,255,255};
int red[3]    = {255,0,0};
int orange[3] = {255,128,0};
int yellow[3] = {255,255,0};
int green1[3] = {128,255,0};
int green2[3] = {0,255,0};
int green3[3] = {0,255,128};
int cyan[3] = {0,255,255};
int blue1[3]   = {0,128,255};
int blue2[3]   = {0,0,255};
int purple[3] = {128,0,255};
int pink[3] = {255,0,255};
int magenta[3] = {255,0,128};

// Set initial color
int redVal = black[0];
int grnVal = black[1]; 
int bluVal = black[2];

int wait = 1;       //  internal crossFade delay; increase for slower fades

// Initialize color variables
int prevR = redVal;
int prevG = grnVal;
int prevB = bluVal;

boolean freeFade = true;

// Set up the LED outputs
void setup()
{
  pinMode(RED_PIN, OUTPUT);   // sets the pins as output
  pinMode(GRN_PIN, OUTPUT);   
  pinMode(BLU_PIN, OUTPUT); 
  
  pinMode(IR_PIN, INPUT);
  pinMode(IR_GROUND, OUTPUT);
  pinMode(IR_POWER, OUTPUT);
  digitalWrite(IR_GROUND, LOW);
  digitalWrite(IR_POWER, HIGH);
  
  Serial.begin(9600);
  crossFade(white);
}

// Main program: list the order of crossfades
void loop() { 
  checkIR();
  if (0) { // Set to 1 for regular fading, comment out previous line
    crossFade(red);
    crossFade(orange);
    crossFade(yellow);
    crossFade(green1);
    crossFade(green2);
    crossFade(green3);
    crossFade(cyan);
    crossFade(blue1);
    crossFade(blue2);
    crossFade(purple);
    crossFade(pink);
    crossFade(magenta);
  }
}

/* To figure out how big a step there should be between one up- or
*  down-tick of one of the LED values, we call calculateStep(), 
*  which calculates the absolute gap between the start and end values, 
*  and then divides that gap by 1020 to determine the size of the step  
*  between adjustments in the value.
*/

int calculateStep(int prevValue, int endValue) {
  int step = endValue - prevValue; // What's the overall gap?
  if (step) {                      // If its non-zero, 
    step = 1020/step;              //   divide by 1020
  } 
  return step;
}

/* The next function is calculateVal. When the loop value, i,
*  reaches the step size appropriate for one of the
*  colors, it increases or decreases the value of that color by 1. 
*  (R, G, and B are each calculated separately.)
*/

int calculateVal(int step, int val, int i) {

  if ((step) && i % step == 0) { // If step is non-zero and its time to change a value,
    if (step > 0) {              //   increment the value if step is positive...
      val += 1;           
    } 
    else if (step  255) {
    val = 255;
  } 
  else if (val < 0) {
    val = 0;
  }
  return val;
}

/* changeColor() instantly changes the color, no fading*/
void changeColor(int color[3]) {
  redVal = color[0];
  grnVal = color[1];
  bluVal = color[2];
  
  if (commonAnode) {
    analogWrite(RED_PIN, redVal);   // Write current values to LED pins
    analogWrite(GRN_PIN, grnVal);      
    analogWrite(BLU_PIN, bluVal); 
  }
  else {
    analogWrite(RED_PIN, 255-redVal);   // Write current values to LED pins
    analogWrite(GRN_PIN, 255-grnVal);      
    analogWrite(BLU_PIN, 255-bluVal); 
  }
}

/* crossFade() converts the percentage colors to a 
*  0-255 range, then loops 1020 times, checking to see if  
*  the value needs to be updated each time, then writing
*  the color values to the correct pins.
*/

void crossFade(int color[3]) {
  // Convert to 0-255
  int R = color[0];
  int G = color[1];
  int B = color[2];

  int stepR = calculateStep(prevR, R);
  int stepG = calculateStep(prevG, G); 
  int stepB = calculateStep(prevB, B);

  for (int i = 0; i <= 1020; i++) {
    redVal = calculateVal(stepR, redVal, i);
    grnVal = calculateVal(stepG, grnVal, i);
    bluVal = calculateVal(stepB, bluVal, i);

    if (commonAnode) {
      analogWrite(RED_PIN, redVal);   // Write current values to LED pins
      analogWrite(GRN_PIN, grnVal);      
      analogWrite(BLU_PIN, bluVal); 
    }
    else {
      analogWrite(RED_PIN, 255-redVal);   // Write current values to LED pins
      analogWrite(GRN_PIN, 255-grnVal);      
      analogWrite(BLU_PIN, 255-bluVal); 
    }
    
    delay(wait); // Pause for 'wait' milliseconds before resuming the loop
  }
  
  // Update current values for next loop
  prevR = redVal; 
  prevG = grnVal; 
  prevB = bluVal;
}

// Begin Infrared stuff
void checkIR() {
  int key = getIRKey(); //Fetch the key
  
  if (key != 0) { //Ignore keys that are zero
    switch(key) {
      case 149: // Power
        changeColor(black);
        break;
      case 144: // CH Up
        crossFade(red);
        break;
      case 145: // CH Down
        crossFade(orange);
        break;
      case 148: // Mute
        crossFade(yellow);
        break;
      case 146: // VOL Right
        crossFade(blue2);
        break;
      case 147: // VOL Left
        crossFade(green2);
        break;
      case 165: // AV/TV
        crossFade(pink);
        break;
    }
  }
}

int getIRKey() {
  int data[12];
  int i;

  while(pulseIn(IR_PIN, LOW) < START_BIT); //Wait for a start bit
  
  for(i = 0 ; i < 11 ; i++)
    data[i] = pulseIn(IR_PIN, LOW); //Start measuring bits, I only want low pulses
  
  for(i = 0 ; i  BIN1_THRES) //is it a 1?
      data[i] = 1;
    else if(data[i] > BIN0_THRES) //is it a 0?
      data[i] = 0;
    else
      return -1; //Flag the data as invalid; I don't know what it is! Return -1 on invalid data
  }

  int result = 0;
  for(i = 0 ; i < 11 ; i++) //Convert data bits to integer
    if(data[i] == 1) result |= (1<<i);

  return result; //Return key number
}

One thought on “Remote controlled crossfading ping pong ball

Comments are closed.