The brains of this remote control is an Arduino Nano using Ken Sharriff's IR library to blink an IR LED (thanks Ken!). Because of the voltage regulator's high quiescent current draw (4.5mA) circuitry was added around the Nano to power down the processor module when not in use. Even if the processor was put in its lowest current "sleep" mode the regulator would kill the battery in less than a week. One fix for this would have been to use an external regulator like a Microchip MCP1702 (significantly lower quiescent current of 2μA), but my junk drawer had more reed relays and diodes than fancy voltage regulators. With the relay setup it takes 0.0μA when idle.

Positive power is always applied to the Nano; the ground signal is switched by three different circuits. When either of the control buttons are pressed, the diodes provide a current path to power up the processor module. The first thing the code does is close the reed relay, activating the third ground signal path so the processor can continue to run even if the button was released. The program then checks which of the two buttons is pressed, sends the appropriate IR signals, then removes power from the relay which powers down the module.

On power-up the stock Nano bootloader spends a couple seconds looking at the USB to see if there are any programming instructions forthcoming before launching into the program. This delay proved a little annoying, so I reflashed it with Optiboot so that the IR signals would start as soon as the power came on / button was pushed.

The case for the TV remote is 3D printed, and has two large colorful buttons of different shapes spaced far apart. Small rubber feet help keep the box from sliding on Easton's wheelchair tray.

Schematic:

Parts List:

Source Code:

#include 
#include 

IRsend irsend;     // Thanks to Ken Sharriff  http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html 
 
int BoardLED = 13;

int IRLED = 3;
int SW_Power = 4;
int SW_Channel = 5;
int PowerControl = 6;

int Repeats = 5;
byte CarrierFrequencyKHz = 56;                // Dish remotes modulate a 56kHz carrier.  My IR receiver parts were all 38kHz -- being able to quickly change this made testing easier
byte MessageLength = 34;
int InterButtonDelay = 500;                   // Time in between digits "pressed" but the remote

byte SW_Channel_State;
byte SW_Power_State;

byte EEPROMChannelMem = 0;
byte SavedChannel;
byte NewChannel;

unsigned int DishRaw[34]      = {350,6200,350,2900,350,   0,350,   0,350,   0,350,   0,350,   0,350,2900,350,2900,350,2900,350,2900,350,2900,350,2900,350,2900,350,2900,350,2900,350,2900};

void setup() {                                // This routine runs once on power up
  pinMode(IRLED, OUTPUT);                     // Connection to anode of IR LED (high = on, low = off) 
  pinMode(BoardLED, OUTPUT);                  // Arduino Nano board has a LED on this pin for diagnostics
  pinMode(PowerControl, OUTPUT);              // Battery is disconnected from board most of the time -- this relay that holds power on once booted up
  pinMode(SW_Channel,INPUT_PULLUP);           // "Change Channel" button, and one of the power-up buttons
  pinMode(SW_Power,INPUT_PULLUP);             // "TV Power Off/On" button, and the other power-up button
  digitalWrite(PowerControl,HIGH);            // Now that we're running (because either "Channel" or "Power" button was pressed turn on relay to keep power on until IR flash sequence is complete 
  Serial.begin(9600);                         // Might want to send out some status/debug messages
}

void DishSendDigit(int Digit) {  // Sends out the flash pattern for one digit (0-9) or special characters (100 = power, 101 = enter)
  int BitCount;
  unsigned long Pattern;
  
  digitalWrite(BoardLED, HIGH);  // Turn on Arduino Nano board LED to show we're sending IR flashes now

  for (BitCount=0; BitCount < Repeats; BitCount++) {
    Pattern = 0;
    switch (Digit) {
      case 1:
         DishRaw[5] = 2900 ;  // Long Long Short Long Long
         DishRaw[7] = 2900 ; 
         DishRaw[9] = 1800 ; 
         DishRaw[11] = 2900 ; 
         DishRaw[13] = 2900 ; 
         irsend.sendRaw(DishRaw,MessageLength,CarrierFrequencyKHz);
         break;
      case 2:
         DishRaw[5] = 2900 ;  // LLSLS
         DishRaw[7] = 2900 ; 
         DishRaw[9] = 1800 ; 
         DishRaw[11] = 2900 ; 
         DishRaw[13] = 1800 ; 
         irsend.sendRaw(DishRaw,MessageLength,CarrierFrequencyKHz);
         break;
      case 3:
         DishRaw[5] = 2900 ;  // LLSSL
         DishRaw[7] = 2900 ; 
         DishRaw[9] = 1800 ; 
         DishRaw[11] = 1800 ; 
         DishRaw[13] = 2900 ; 
         irsend.sendRaw(DishRaw,MessageLength,CarrierFrequencyKHz);
         break;
      case 4:
         DishRaw[5] = 2900 ;  // LSLLL
         DishRaw[7] = 1800 ; 
         DishRaw[9] = 2900 ; 
         DishRaw[11] = 2900 ; 
         DishRaw[13] = 2900 ; 
         irsend.sendRaw(DishRaw,MessageLength,CarrierFrequencyKHz);
         break;
      case 5:
         DishRaw[5] = 2900 ;  // LSLLS
         DishRaw[7] = 1800 ; 
         DishRaw[9] = 2900 ; 
         DishRaw[11] = 2900 ; 
         DishRaw[13] = 1800 ; 
         irsend.sendRaw(DishRaw,MessageLength,CarrierFrequencyKHz);
         break;
      case 6:
         DishRaw[5] = 2900 ;  // LSLSL
         DishRaw[7] = 1800 ; 
         DishRaw[9] = 2900 ; 
         DishRaw[11] = 1800 ; 
         DishRaw[13] = 2900 ; 
         irsend.sendRaw(DishRaw,MessageLength,CarrierFrequencyKHz);
         break;
      case 7:
         DishRaw[5] = 2900 ;  // LSSLL
         DishRaw[7] = 1800 ; 
         DishRaw[9] = 1800 ; 
         DishRaw[11] = 2900 ; 
         DishRaw[13] = 2900 ; 
         irsend.sendRaw(DishRaw,MessageLength,CarrierFrequencyKHz);
         break;
      case 8:
         DishRaw[5] = 2900 ;  // LSSLS
         DishRaw[7] = 1800 ; 
         DishRaw[9] = 1800 ; 
         DishRaw[11] = 2900 ; 
         DishRaw[13] = 1800 ; 
         irsend.sendRaw(DishRaw,MessageLength,CarrierFrequencyKHz);
         break;
      case 9:
         DishRaw[5] = 2900 ;  // LSSSL
         DishRaw[7] = 1800 ; 
         DishRaw[9] = 1800 ; 
         DishRaw[11] = 1800 ; 
         DishRaw[13] = 2900 ; 
         irsend.sendRaw(DishRaw,MessageLength,CarrierFrequencyKHz);
         break;
      case 0:    
         DishRaw[5] = 1800 ;  // SLLLS
         DishRaw[7] = 2900 ; 
         DishRaw[9] = 2900 ; 
         DishRaw[11] = 2900 ; 
         DishRaw[13] = 1800 ; 
         irsend.sendRaw(DishRaw,MessageLength,CarrierFrequencyKHz);
         break;
      case 100:  // Power
         DishRaw[5] = 2900 ;  // LLLSL
         DishRaw[7] = 2900 ; 
         DishRaw[9] = 2900 ; 
         DishRaw[11] = 1800 ; 
         DishRaw[13] = 2900 ; 
         irsend.sendRaw(DishRaw,MessageLength,CarrierFrequencyKHz);
         break;
      case 101:  // Enter
         DishRaw[5] = 1800 ;  // SLLLL
         DishRaw[7] = 2900 ; 
         DishRaw[9] = 2900 ; 
         DishRaw[11] = 2900 ; 
         DishRaw[13] = 2900 ; 
         irsend.sendRaw(DishRaw,MessageLength,CarrierFrequencyKHz);
         break;
    }
  }
  digitalWrite(BoardLED, LOW);     // Turn off board LED indicating flashing complete
};

void loop() {
  
  delay (5);
  
  // Look at the buttons to see which one powered us up
  SW_Channel_State = digitalRead(SW_Channel);
  SW_Power_State = digitalRead(SW_Power);
  
  if ((SW_Channel_State == 0) || (SW_Power_State == 0)) {  // Hopefully one of them was low!
    if (SW_Channel_State == 0) {  // It was the channel switch
      Serial.print("Channel: ");
 
      SavedChannel = EEPROM.read(EEPROMChannelMem);    // Retrieve the last channel sent from the EEPROM location
      switch (SavedChannel) {                          // Decide the next channel.  Might be inefficient compared to ++ but ready for more non-sequential channels to be added.
        case 169:
          NewChannel = 170;  // NICK East
          break;
        case 170:
          NewChannel = 171;  // NICK West 
          break;
        case 171:
          NewChannel = 172;  // Disney
          break;
        default:
          NewChannel = 169;   // Nick Jr.
          break;
      }   
      
      Serial.print("Saved ");
      Serial.print(SavedChannel, DEC);   
      Serial.print(" New ");
      Serial.print(NewChannel, DEC);
    
      EEPROM.write(EEPROMChannelMem, NewChannel);       // Save it back to the EEPROM location, good for over 1M channel changes.  http://hackaday.com/2011/05/16/destroying-an-arduinos-eeprom
  
      Serial.print (" SEND ");
    
      int D1 = NewChannel / 100;               // Convert the channel into three digits (D1 D2 D3)
      NewChannel = NewChannel - D1 * 100;
      int D2 = NewChannel / 10;
      NewChannel = NewChannel - D2 * 10;
      int D3 = NewChannel;

      DishSendDigit(D1);                       // Send first digit
      delay(InterButtonDelay);
      DishSendDigit(D2);                       // Send second digit
      delay(InterButtonDelay);
      DishSendDigit(D3);                       // Send last digit
      delay(InterButtonDelay); 
      DishSendDigit(101);                      // ENTER
   
    } else {
      if (SW_Power_State == 0) {                // The other push button woke us up
        Serial.print("Power: ");
        Serial.print (" SEND ");
        DishSendDigit(100);  // POWER       
      } 
      Serial.print(" Done ");
    }
 
    // If one or the other button is still down, hang out until the all clear sounds
    while ((digitalRead(SW_Channel) == 0) || (digitalRead(SW_Power) == 0)) {
      Serial.print (".");
      delay (100);
    };
  
    Serial.println(" End\n\r");          // We're going to sleep
    delay (100);                         // Let the message go out
    digitalWrite(PowerControl,LOW);      // Hari-kari moment
    delay (100);                         // Wait for capacitors to discharge
  }
}

Special thanks again to Ken who made the IR Library for Arduino, and Greg Meineke of Meineke Electronics for letting me test in their showroom. And the Arduino folks (both the .org-s and the .cc-s).

I am but "nanos gigantum humeris insidentes."