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:
- Arduino Nano
- LED (850 nm, Infrared)
- Resistor (180 ohm ¼ Watt)
- Diodes (3) (1N4001 or equivalent)
- Relay (5V low current, Magnecraft W172DIP-5 or equivalent )
- Push Buttons (All Electronics LPS-5Bor equivalent )
- 9V battery and battery clip
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."