/*
RGB Wave
Firmware
for use with ATtiny2313
AS220
Mitch Altman
29-Aug-08

Distributed under Creative Commons 2.5 -- Attib & Share Alike
*/


/*
For this project, imagine a bunch of little lights (maybe 20 or 40 of them), 
on a table, each about the size of a chess piece.  
Each is independent of the other.
You arrange them around on the table any way you want.
Each one continually slowly changes colors on its own.
When you wave your hand over them, it creates waves of colors that follow your hand.
*/


/*
I added an IR emitter and IR detector to the Trippy RGB Light to create this project.
The Trippy RGB Light uses 3 LEDs, one Red, one Green, one Blue, each fading at different rates
so that when the light from the three LEDs add together, you get a lot of changing colors
(you can get any color a computer monitor can get 
using different brightnesses of Red, Green, and Blue light -- 
this is the way computer monitors create color).  Trippy!
For this project instead of three LEDs, I used one RGB LED 
(which is one package containing a Red, a Green, and a Blue LED, all in one package).
I connected an IR detector to an input pin of the ATtiny2313 microcontroller (pointint up), 
so that when it detects IR light, it is programmed to reset the microcontroller, 
which starts the sequence of changing colors from the beginning.
I connected an IR emitter to an output pin of the microcontroller (pointing up),
so that the IR light will reflect off of your hand when you wave it over the device.
As a result, if you have a whole bunch of these devices on a table, 
when you wave your hand over the devices, it will look like a wave of color following you hand.
*/


/*
This project provides a good example of how to use Pulse Width Modulation (PWM)
to fade a voltage up or down (in this case, the changing voltage is used to 
control the brightness of Red, Green, and Blue LEDs).

We will use one of the two hardware timers that are embedded inside of the ATtiny2313,
set up to control PWM for the Red LED and the Green LED.  Since there are only two 
timer compare regsiters on the one hardware timer, we will create PWM for the Blue LED 
by manually pulsing it in a firmware loop.
(The other hardware timer will be used to pulse an IR emitter at 38KHz.)

With PWM, we vary the amount of time an LED is on versus how long it is off.  
We repeatedly turn an LED on and off very quickly (so quickly you can't see it blink).
If the LED is on 50% of the time (and off for 50% of the time), 
  it will be half as bright as if it were on all of the time.
If the LED is on 25% of the time (and off for 75% of the time), 
  it will be one quarter as bright as if it were on all of the time.
Each time we turn the LED on and off, the amount of time it is on, 
  added to the amount of time it is off, always equals the same length of time 
  (this length of time is known as the "PWM period").
It doesn't really matter what the PWM period is, 
  as long as it is short enough that we can't perceive the the LED flicker.
  We can perceive flickering if an LED blinks slower than about 1/30 of a second, 
  or about 0.033 seconds (or 30Hz).  
  In this firmware, I keep the PWM period to less than 0.01 seconds (faster than 100Hz)
    so there is no perceieved flicker (though it is interesting to wave the unit back 
    and forth while it is running, and you can actually see the PWM pusle widths widen 
    and shrink over time).
*/


/*
The project uses one IR emitter and one IR detector.

The IR detector I chose is very sensitive to IR light that is pulsing at about 38KHz,
and it is very insensitive to IR light that is not pulsing near 38KHz.

The IR emitter is pointed up, and is constantly outputing IR light pusling at about 38KHz.
The IR detector is also pointed up, and has one output, 
which is Low when it sees 38KHz IR light reflecting from above 
(such as when you wave your hand over it, thus reflecting the IR light from the IR emitter),
and the output is High when it does not see 38KHz IR light.

Since the output of the IR detector is connected to an input pin, when the output from the 
IR detector goes Low, the firmware can detect this, and restart the firmware from the 
beginning, and the net effect is that you see a wave of colors move under you hand
(assuming that there are a lot of these devices near each other on a table).
*/


/*
For this project, I hacked a previous project of mine, called Trippy RGB Light.
The Trippy RGB Light was hacked from Ladyada's MiniPOV3 kit.

Ladyada has a great website about the MiniPOV kit, from which this project was hacked:
http://ladyada.net/make/minipov3/index.html
Her website also has a user forum, where people can ask and answer questions by people building
various projects, including this one:
http://www.ladyada.net/forums/
(Click on the link for MiniPOV).
*/


#include <avr/io.h>             // this contains all the IO port definitions
#include <avr/interrupt.h>      // definitions for interrupts
#include <avr/sleep.h>          // definitions for power-down modes
#include <avr/pgmspace.h>       // definitions or keeping constants in program memory

// this global variable is normally 0, but when the IR detector sees IR, 
//   its output brings PD0 Low.  It is convient to check for PD0 Low in the pulseBlue() function,
//   and if it sees PD1 Low, it sets Start_Over = 1.
int Start_Over = 0;

/*
This project has one RGB LED.
The light from the LED should be diffused so that the light from 
it mixes together (a short section of white drinking straw works well).
The firmware goes through a sequence, mixing various amounts of brightness 
of R, G, and B to create various different colors, and blinking and fading
these colors to create a trippy effect.
You can easily change this firmware to create your own sequences of colors!
*/



/*
Parts list for this RGB Light project:
1   ATtiny2313
1   CR2032 coin-cell battery
1   CR2032 battery holder
1   small slide switch (for on-off)
1   RGB LED (common cathode)
1   white drinking straw
1   IR333-A IR emitter
1   TSOP32138 IR detector
*/


/*
The hardware for this project is very simple:
     ATtiny2313 has 20 pins:
       pin 2   PD0 - connects to the output of the IR detector (pin 1 of the detector)
       pin 10  ground (connects to common cathode lead of RGB LED, and ground of the IR detector)
       pin 13  PB1 -  IR emitter (cathode)
       pin 14  OC0A - IR emitter (anode)
       pin 15  OC1A - Red lead of RGB LED (anode)
       pin 16  OC1B - Green lead of RGB LED (anode)
       pin 17  PB5 - Blue lead of RGB LED (anode)
       pin 20  +3v
    All other pins are unused

    This firmware requires that the clock frequency of the ATtiny 
      is the default that it is shipped with:  8.0MHz
*/



/*
The C compiler creates code that will transfer all constants into RAM when the microcontroller
resets.  Since this firmware has a table (lightTab) that is too large to transfer into RAM
(and since we don't need it in RAM) the C compiler needs to be told to keep it in program 
memory space.  This is accomplished by the macro PROGMEM (this is used, below, in the 
definition for the lightTab).  Since the C compiler assumes that constants are in RAM, rather 
than in program memory, when accessing the lightTab, we need to use the pgm_read_byte() macro, 
and we need to use the lightTab as an address, i.e., precede it with "&".  For example, to 
access lightTab[3].red, which is a byte, this is how to do it:
     pgm_read_byte( &lightTab[3].red );
And to access lightTab[3].fadeTime, which is a word, this is how to do it:
     pgm_read_word( &lightTab[3].fadeTime );
*/



/*
The following Light Table consists of any number of rgbElements that will fit into the 
2k flash ROM.
Each rgbElement consists of:
     fadeTime -- how long to take to fade from previous values of RGB 
                   to the ones specified in this rgbElement 
                   (0 or between 1,000 and 65,535)
     holdTime -- how long to keep the RGB values once they are faded in 
                   (0 or between 1,000 and 65,535)
     Red      -- brightness value for Red   (between 0 to 255)
     Green    -- brightness value for Green (between 0 to 255)
     Blue     -- brightness value for Blue  (between 0 to 255)
Both of the time values, fadeTime and holdTime, are expressed as the number of 400 
microseconds -- for example, 2 seconds would be entered as 5000.  
To signify the last rgbElement in the lightTab, its fadeTime and hold time must both 
be 0 (all other values of this last rgbElement are ignored).

The values for fadeTime and holdTime must either be 0, or between 1,000 and 65,535 
(i.e., 0, or between 0.4 sec and 26.2 sec).
*/

/* 
  The Light Sequences and the notions of fadeTime and holdTime 
  are taken from Pete Griffiths, downloaded from:
  http://www.petesworld.demon.co.uk/homebrew/PIC/rgb/index.htm

  I modified it to fit my purposes.

  The sequence takes about 2 minutes.
  More precisely:
       adding all of the fadeTime values together:  121,000
       adding all of the holdTime values together:  138,000
       adding these together = 259,000.
  Since the time values are each 400 microseconds, 259,000 is 103.6 seconds, 
    or, 1.727 minutes, which is 1 minute, 44 seconds.

  The Main function repeats the sequence several times.
*/


// table of Light Sequences
struct rgbElement {
  int fadeTime;            // how long to fade from previous values of RGB to the ones specified in this rgbElement (0 to 65,535)
  int holdTime;            // how long to keep the RGB values once they are faded in (0 or 1000 to 65,535)
  unsigned char red;       // brightness value for Red LED (0 to 255)
  unsigned char green;     // brightness value for Green LED (0 to 255)
  unsigned char blue;      // brightness value for Blue LED (0 to 255)
} const lightTab[] PROGMEM = {
  {     0,   500, 255,   0,   0 },
  {   500,     0,   0,   0,   0 },
  {     0,   500,   0, 255,   0 },
  {   500,     0,   0,   0,   0 },
  {     0,   500,   0,   0, 255 },
  {   500,     0,   0,   0,   0 },
  {  2500,  2500, 255,   0,   0 },
  {  2500,  2500,   0, 255,   0 },
  {  2500,  2500,   0,   0, 255 },
  {  2500,  2500, 255,  64,   0 },
  {  2500,  2500,  64, 255,  64 },
  {  2500,  2500,   0,  64, 255 },
  {  2500,  2500,  64,   0,  64 },
  {     0,  1500, 255,   0,   0 },
  {     0,  1500,   0, 255,   0 },
  {     0,  1500,   0,   0, 255 },
  {     0,  1500, 240,   0, 240 },
  {     0,  1500, 255, 155,   0 },
  {     0,  1500, 255, 255, 255 },
  {     0,  1500, 128, 128, 128 },
  {     0,  1500,  48,  48,  58 },
  {     0,  1500,   0,   0,   0 },
  {  2500,  2500, 255,   0,   0 },
  {  2500,  2500, 255, 255,   0 },
  {  2500,  2500,   0, 255,   0 },
  {  2500,  2500,   0, 255, 255 },
  {  2500,  2500,   0,   0, 255 },
  {  2500,  2500, 255,   0, 255 },
  {  2500,     0,   0,   0,   0 },
  {  2500,  2500, 255,   0,   0 },
  {  2500,  2500, 255, 255,   0 },
  {  2500,  2500,   0, 255,   0 },
  {  2500,  2500,   0, 255, 255 },
  {  2500,  2500,   0,   0, 255 },
  {  2500,  2500, 255,   0, 255 },
  {  2500,     0,   0,   0,   0 },
  {  2500,  2500, 254,  32,   0 },
  {  2500,  2500, 254, 128,   0 },
  {  2500,  2500, 254, 240,   0 },
  {  2500,  2500, 128, 240,   0 },
  {     0,  2500,   0,   0,   0 },
  {  2500,  2500,   0,  16, 255 },
  {  2500,  2500,   0, 128, 255 },
  {  2500,  2500,   0, 240, 128 },
  {  2500,  2500,  16,  16, 240 },
  {  2500,  2500, 240,  16, 240 },
  {  2500,  2500,  64,   0, 250 },
  {     0,  2500,  10,  10,  10 },
  {     0,  2500,   0,   0,   0 },
  {  2500,  2500, 240,   0, 240 },
  {  2500,  2500,  32,   0, 240 },
  {  2500,  2500, 128,   0, 128 },
  {  2500,  2500, 240,   0,  32 },
  {  2500,     0,   0,   0,  10 },
  {  2500,     0,   0,   0,   0 },
  {  1000,  1000,   0,   0,   0 },
  {  1000,  1000,  32,   0,   0 },
  {  1000,  1000,  64,   0,   0 },
  {     0,  1000,  96,   0,   0 },
  {  1000,     0, 128,   0,   0 },
  {  1000,     0, 160,  32,   0 },
  {  1000,     0, 192,  64,   0 },
  {  1000,     0, 224,  96,   0 },
  {     0,  1000, 255, 128,   0 },
  {  1000,  1000,   0, 160,   0 },
  {     0,  1000,   0, 192,   0 },
  {  1000,  1000,   0, 224,  32 },
  {  1000,     0,   0, 255,  64 },
  {  1000,     0,   0,   0,  96 },
  {  1000,     0,   0,   0, 128 },
  {  1000,     0,   0,   0, 160 },
  {  1000,     0,   0,   0, 192 },
  {  1000,     0,   0,   0, 224 },
  {  1000,  1000,   0,   0, 255 },
  {  1000,     0,   0,   0,   0 },
  {     0,  1000,   0,   0, 255 },
  {  1000,  1000,  32,   0,   0 },
  {  1000,  1000,  96,   0,   0 },
  {  1000,  1000, 160,   0,   0 },
  {  1000,     0, 255,   0,   0 },
  {  1000,  1000,   0,  96,   0 },
  {  1000,  1000,   0, 160,  32 },
  {  1000,  1000,   0, 224,  64 },
  {  1000,  1000,   0, 255,  96 },
  {  1000,  1000,   0,   0, 128 },
  {  1000,  1000,   0,   0, 160 },
  {     0,  1000,   0,  32, 192 },
  {     0,  1000,   0,  64, 224 },
  {     0,  1000,   0,  96, 225 },
  {     0,  1000,   0, 128,   0 },
  {     0,  1000,   0, 160,   0 },
  {     0,  1000,   0, 192,  32 },
  {     0,  1000,   0, 224,  64 },
  {     0,  1000,   0, 255,  96 },
  {     0,  1000,   0,   0, 128 },
  {     0,  1000,   0,   0, 160 },
  {     0,  1000,   0,   0, 192 },
  {     0,  1000,   0,   0, 224 },
  {     0,  1000,   0,   0, 255 },
  {     0,  1000,   0,   0,   0 },
  {     0,     0,   0,   0,   0 }
};



// This function delays the specified number of 10 microseconds
void delay_ten_us(unsigned long int us) {
  unsigned long int count;
  const unsigned long int DelayCount=6;  // this value was determined by trial and error

  while (us != 0) {
    // Toggling PD1 is done here to force the compiler to do this loop, rather than optimize it away
    for (count=0; count <= DelayCount; count++) {PIND |= 0b00000010;};
    us--;
  }
}



// This function delays (1.56 microseconds * x) + 2 microseconds
//   (determined empirically)
//    e.g.  if x = 1, the delay is (1 * 1.56) + 2 = 5.1 microseconds
//          if x = 255, the delay is (255 * 1.56) + 2 = 399.8 microseconds
void delay_x_us(unsigned long int x) {
  unsigned long int count;
  const unsigned long int DelayCount=0;  // the shortest delay

  while (x != 0) {
    // Toggling PD1 is done here to force the compiler to do this loop, rather than optimize it away
    for (count=0; count <= DelayCount; count++) {PIND |= 0b00000010;};
    x--;
  }
}



// This function pulses the Blue LED on PB5 (pin 17)
// Since Blue LED is not on a PWM pin on a hardware timer, we need to pulse it manually.
//   We pulse it High for [Blue value], and pulse it Low for [255 - Blue value].
//   Since the delay_x_us function delays 1.56x+2 microseconds,
//     the total period is about 400 microseconds, which is 2500Hz (if we repeat it)
//     (and that is way fast enough so that we don't perceive the Blue LED flicker).
void pulseBlue(unsigned char blueVal) {
  PORTB |= 0b00100000;  // turn on Blue LED at PB5 (pin 17) for 4 * Blue value
  delay_x_us( blueVal );
  PORTB &= 0b11011111;  // turn off Blue LED at PB5 for 4 * (255 - Blue value)
  delay_x_us( (255 - blueVal) );
  // if IR detector output is Low (at PD0), it is seeing IR from an IR emitter
  // so let the firmware know to reset and start over
  if (!(PIND & 0b00000001)) {  
    Start_Over = 1;
  }
  else {
    Start_Over = 0;
  }
}



// This function turns off the IR emitter
// Since the cathode of the IR emitter is connected to PB1, making PB1 High turns it off
void IR_off(void) {
  PORTB |= 0b00000010;  // turn off IR emitter by bringing its cathode High (PB1)
}



// This function turns on the IR emitter
// Since the cathode of the IR emitter is connected to PB1, making PB1 Low turns it on
void IR_on(void) {
  PORTB &= 0b11111101;  // turn on IR emitter by bringing its cathode Low (PB1)
}



// This function sends one rgbElement of lightTab to the LEDs, 
//   given index to the codeElement in lightTab
//     If both fadeTime = 0 and holdTime = 0 that signifies the last rgbElement of the lightTab
//
// There are several variables used in this function:
//   index:     the input argument for this function -- it is the index to lightTab, pointing to the rgbElement from lightTab
//
//   FadeTime:  gotten from rgbElement in lightTab
//   HoldTime:  gotten from rgbElement in lightTab
//   Red:       gotten from rgbElement in lightTab
//   Green:     gotten from rgbElement in lightTab
//   Blue:      gotten from rgbElement in lightTab
//
//   redPrev:   gotten from previous rgbElement in lightTab (set to 0 if index to lightTab = 0)
//   greenPrev: gotten from previous rgbElement in lightTab (set to 0 if index to lightTab = 0)
//   bluePrev:  gotten from previous rgbElement in lightTab (set to 0 if index to lightTab = 0)
//
//   redTime:   when the fadeCounter in the fade loop reaches this value, we will update the Red LED brightness value
//   greenTime: when the fadeCounter in the fade loop reaches this value, we will update the Green LED brightness value
//   blueTime:  when the fadeCounter in the fade loop reaches this value, we will update the Blue LED brightness value
//
//   redTemp:   keep track of current Red LED brightness value as we're fading up or down in the fade loop
//   greenTemp: keep track of current Green LED brightness value as we're fading up or down in the fade loop
//   blueTemp:  keep track of current Blue LED brightness value as we're fading up or down in the fade loop
//
//   redDelta:   the total amount of brightness we need to change to get from where the Red LED was to where we want it
//   greenDelta: the total amount of brightness we need to change to get from where the Green LED was to where we want it
//   blueDelta:  the total amount of brightness we need to change to get from where the Blue LED was to where we want it
//
//   redTimeInc:   in the fade loop we will update the Red LED every time the fadeCounter increments by this amount
//   greenTimeInc: in the fade loop we will update the Green LED every time the fadeCounter increments by this amount
//   blueTimeInc:  in the fade loop we will update the Blue LED every time the fadeCounter increments by this amount
//
//   fadeCounter:  for counting through the steps (each of which is 400us long) in the fade loop
//
//   holdCounter:  for counting through the steps (each of which is 400us long) in the hold loop


void sendrgbElement( int index ) {
  // get values of rgbElement from lightTab
  int FadeTime = pgm_read_word(&lightTab[index].fadeTime);
  int HoldTime = pgm_read_word(&lightTab[index].holdTime);
  unsigned char Red = pgm_read_byte(&lightTab[index].red);
  unsigned char Green = pgm_read_byte(&lightTab[index].green);
  unsigned char Blue = pgm_read_byte(&lightTab[index].blue);

  // get previous RGB brightness values from lightTab
  //   (these values are set to 0 if index to lightTab = 0)
  unsigned char redPrev = 0;    // keep track of previous Red brightness value
  unsigned char greenPrev = 0;  // keep track of previous Green brightness value
  unsigned char bluePrev = 0;   // keep track of previous Blue brightness value
  if (index != 0) {
    redPrev = pgm_read_byte(&lightTab[index-1].red);    
    greenPrev = pgm_read_byte(&lightTab[index-1].green);
    bluePrev = pgm_read_byte(&lightTab[index-1].blue);  
  }

  // set color timing values
  //   everytime the fadeCounter reaches this timing value in the fade loop
  //   we will update the color value for the color (default value of 0 for no updating)
  int redTime = 0;
  int greenTime = 0;
  int blueTime = 0;

  // set values of temp colors 
  //   starting from the previous color values, 
  //   these will change to the color values just gotten from rgbElement over fadeTime
  unsigned char redTemp = redPrev;
  unsigned char greenTemp = greenPrev;
  unsigned char blueTemp = bluePrev;

  // fade LEDs up or down, from previous values to current values
  int redDelta = Red - redPrev;                // total amount to fade red value (up or down) during fadeTime
  int greenDelta = Green - greenPrev;          // total amount to fade green value (up or down) during fadeTime
  int blueDelta = Blue - bluePrev;             // total amount to fade blue value (up or down) during fadeTime

  if (redDelta != 0) {
    redTime = (FadeTime / redDelta);           // increment Red value every time we reach this fade value in the fade loop
    if (redTime < 0) redTime = -redTime;       //    absolute value
    redTime = redTime + 1;                     // adjust for truncation of integer division
  }                                            //
  int redTimeInc = redTime;                    // increment Red value every time the fadeCounter increments this amount

  if (greenDelta != 0) {
    greenTime = (FadeTime / greenDelta);       // increment Green value every time we reach this fade value in the fade loop
    if (greenTime < 0) greenTime = -greenTime; //    absolute value
    greenTime = greenTime + 1;                 // adjust for truncation of integer division
  }                                            //
  int greenTimeInc = greenTime;                // increment Green value every time the fadeCounter increments this amount

  if (blueDelta != 0) {
    blueTime = (FadeTime / blueDelta);         // increment Blue value every time we reach this fade value in the fade loop
    if (blueTime < 0) blueTime = -blueTime;    //    absolute value
    blueTime = blueTime + 1;                   // adjust for truncation of integer division
  }                                            //
  int blueTimeInc = blueTime;                  // increment Blue value every time the fade value increments this amount

  // set color increment values
  //   the amount to increment color value each time we update it in the fade loop
  //   (default value of 1, to slowly increase brightness each time through the fade loop)
  unsigned char redInc = 1;     
  unsigned char greenInc = 1;
  unsigned char blueInc = 1;
  // if we need to fade down the brightness, then make the increment values negative
  if (redDelta < 0) redInc = -1;
  if (greenDelta < 0) greenInc = -1;
  if (blueDelta < 0) blueInc = -1;

  // if FadeTime = 0, then just set the LEDs blinking at the RGB values (the fade loop will not be executed)
  if (FadeTime == 0) {
    blueTemp = Blue; // no need to manually pulse Blue LED on PB5 (pin 17) now, since it will be done in the hold loop
    OCR1A = Red;     // update PWM for Red LED on OC1A (pin 15)
    OCR1B = Green;   // update PWM for Green LED on OC1B (pin 16)
  }

  // fade loop
  //   this loop will independently fade each LED up or down according to all of the above variables
  //   (it will take a length of time, FadeTime, to accomplish the task)
  //   this loop is not executed if FadeTime = 0 (since 1 is not <= 0, in the "for" loop)
  for (int fadeCounter=1; fadeCounter<=FadeTime; fadeCounter++) {
    if ( fadeCounter == redTime ) {
      redTemp = redTemp + redInc;                 // increment to next red value
      redTime = redTime + redTimeInc;             // we'll increment Red value again when FadeTime reaches new redTime
    }
    if ( fadeCounter == greenTime ) {
      greenTemp = greenTemp + greenInc;           // increment to next green value                                      
      greenTime = greenTime + greenTimeInc;       // we'll increment Green value again when FadeTime reaches new greenTime
    }
    if ( fadeCounter == blueTime ) {
      blueTemp = blueTemp + blueInc;              // increment to next blue value                                      
      blueTime = blueTime + blueTimeInc;          // we'll increment Blue value again when FadeTime reaches new blueTime
    }
    pulseBlue(blueTemp); // one manual PWM pulse on the Blue LED on PB5 (pin 17) for a period of 400 microseconds
    OCR1A = redTemp;     // update PWM for Red LED on OC1A (pin 15)
    OCR1B = greenTemp;   // update PWM for Green LED on OC1B (pin 16)
    if ( Start_Over ) return;  // if the IR detector saw IR, then we should reset and start over
  }
  OCR1A = Red;    // leave Timer1 PWM at final brightness value for Red (in case there were rounding errors in above math)
  OCR1B = Green;  // leave Timer1 PWM at final brightness value for Green (in case there were rounding errors in above math)

  // hold loop
  //   hold all LEDs at current values for HoldTime
  for (int holdCounter=0; holdCounter<HoldTime; holdCounter++) {
    pulseBlue(blueTemp); // one manual PWM pulse on the Blue LED on PB5 (pin 17) for a period of 400 microseconds
                         // the Red LED will continue to pulse automatically from the hardware Timer1
                         // the Green LED will continue to pulse automatically from the hardware Timer1
    if ( Start_Over ) return;  // if the IR detector saw IR, then we should reset and start over
  }
}



int main(void) {
start_over:
  Start_Over = 0;       // this global var gets set to 1 in the pulseBlue() function is the IR detector sees IR
  TIMSK = 0x00;         // no Timer interrupts enabled
  DDRB = 0xFF;          // set all PortB pins as outputs
  PORTB = 0x00;         // all PORTB output pins Low

  // turn off IR emitter
  IR_off();             // this turns off the IR emitter (with its anode on PB2) by bringing its cathode on PB1 high

  // start up Timer0 in CTC Mode at about 38KHz to drive the IR emitter on output OC0A:
  //   8-bit Timer0 OC0A (PB2, pin 14) is set up for CTC mode, toggling output on each compare
  //   Fclk = Clock = 8MHz
  //   Prescale = 1
  //   OCR0A = 104
  //   F = Fclk / (2 * Prescale * (1 + OCR0A) ) = 38KHz
  TCCR0A = 0b01000010;  // COM0A1:0=01 to toggle OC0A on Compare Match
                        // COM0B1:0=00 to disconnect OC0B
                        // bits 3:2 are unused
                        // WGM01:00=10 for CTC Mode (WGM02=0 in TCCR0B)
  TCCR0B = 0b00000001;  // FOC0A=0 (no force compare)
                        // F0C0B=0 (no force compare)
                        // bits 5:4 are unused
                        // WGM2=0 for CTC Mode (WGM01:00=10 in TCCR0A)
                        // CS02:00=001 for divide by 1 prescaler (this starts Timer0)
  OCR0A = 104;  // to output 38,095.2KHz on OC0A (PB2, pin 14)

  // start up Timer1 in Fast PWM Mode at 122Hz to drive Red LED on output OC1A and Green LED on output OC1B:
  //   16-bit Timer1 OC1A (PB3, pin 15) and OC1B (PB4, pin 16) are set up for Fast PWM mode, with 8-bit resolution
  //   Fclk = Clock = 8MHz
  //   Prescale = 256
  //   TOP = 255
  //   OCR1A =  0 (to start out -- this value will increase to increase brightness of Red LED)
  //   OCR1B =  0 (to start out -- this value will increase to increase brightness of Green LED)
  //   F = Fclk / (Prescale * (TOP+1) ) = 122Hz
  // There is nothing too important about driving the Red and Green LEDs at 122Hz, it is somewhat arbitrary,
  //   but it is fast enough to make it seem that the Red and Green LEDs are not flickering.
  // Later in the firmware, the OCR1A and OCR1B compare register values will change, 
  //   but the period for Timer1 will always remain the same (with F = 122Hz, always) --
  //   with OCR1A = 0, the portion of the period with the Red LED on is a minimum
  //     so the Red LED is very dim,
  //   with OCR1A = 255, the portion of the period with the Red LED on is a maximum
  //     so the Red LED is very bright.
  //   with OCR1B = 0, the portion of the period with the Green LED on is a minimum
  //     so the Green LED is very dim,
  //   with OCR1B = 255, the portion of the period with the Green LED on is a maximum
  //     so the Green LED is very bright.
  //   the brightness of the Red LED can be any brightness between the min and max
  //     by varying the value of OCR1A between 0 and 255.
  //   the brightness of the Green LED can be any brightness between the min and max
  //     by varying the value of OCR1B between 0 and 255.
  TCCR1A = 0b10100001;  // COM1A1:0=10 for non-inverting PWM on OC1A (Red LED output pin)
                        // COM1B1:0=10 for non-inverting PWM on OC1B (Green LED output pin)
                        // bits 3:2 are unused
                        // WGM11:10=01 for Fast PWM Mode with 8-bit resolution, which makes TOP=255 (WGM13:12=01 in TCCR1B)
  TCCR1B = 0b00001100;  // ICNC1=0 (no Noise Canceller)
                        // ICES1=0 (don't care about Input Capture Edge)
                        // bit 5 is unused
                        // WGM13:12=01 for Fast PWM Mode with 8-bit resolution (WGM11:10=01 in TCCR1A)
                        // CS12:10=100 for divide by 256 prescaler (this starts Timer1)
  TCCR1C = 0b00000000;  // FOC1A=0 (no Force Output Compare for Channel A)
                        // FOC1B=0 (no Force Output Compare for Channel B)
                        // bits 5:0 are unused
  OCR1A = 0;  // start with minimum brightness for Red LED on OC1A (PB3, pin 15)
  OCR1B = 0;  // start with minimum brightness for Green LED on OC1B (PB4, pin 16)

  // turn on IR emitter
  IR_on();              // this turns on the IR emitter (with its anode on PB2) by bringing its cathode on PB1 Low

  // Since we are only using hardware timers to drive the Red and Green LEDs with PWM
  //   we will pulse the Blue LED manually with the firmware.
  int index = 0;  // this points to the next rgbElement in the lightTab (initially pointing to the first rgbElement)
  // send the entire 1.75-minute sequence 180 times so that it lasts about 5 hours
  for (int count=0; count<180; count++) {
    // send all rgbElements to LEDs (when both fadeTime = 0 and holdTime = 0 it signifies the last rgbElement in lightTab)
    do {
      sendrgbElement(index);        // send one rgbElement to LEDs
      index++;                      // increment to point to next rgbElement in lightTab
      if ( Start_Over ) break;      // if the IR detector saw IR, then we should reset and start over
    } while ( !( (pgm_read_word(&lightTab[index].fadeTime) == 0 ) && (pgm_read_word(&lightTab[index].holdTime) == 0 ) ) );
    if ( Start_Over ) break;      // if the IR detector saw IR, then we should reset and start over
    index = 0;
  }
  if ( Start_Over ) goto start_over;    // if the IR detector saw IR, then we should reset and start over

  // Shut down everything and put the CPU to sleep
  TCCR0B &= 0b11111000;  // CS02:CS00=000 to stop Timer0 (turn off IR emitter)
  TCCR0A &= 0b00111111;  // COM0A1:0=00 to disconnect OC0A from PB2 (pin 14)
  TCCR1B &= 0b11111000;  // CS12:CS10=000 to stop Timer1 (turn off Red and Green LEDs)
  TCCR1A &= 0b00001111;  // COM1A1:0=00 to disconnect OC1A from PB3 (pin 15)
                         // COM1B1:0=00 to disconnect OC1B from PB4 (pin 16)
  MCUCR |= 0b00100000;   // SE=1 (bit 5)
  MCUCR |= 0b00010000;   // SM1:0=01 to enable Power Down Sleep Mode (bits 6, 4)
  delay_ten_us(10000);   // wait .1 second
  PORTB = 0x00;          // all PORTB outputs Low
  DDRB = 0x00;           // make PORTB all inputs
  sleep_cpu();           // put CPU into Power Down Sleep Mode
}


