#include #include #define FLASH_LED /* * We simulate sending IR commands from a specific remote control. * In this case it's the Remote control used by a View21 FreeView Set Top Box. * These are sold in outlets like Currys in the UK. * They use the NEC protocol. */ #define CUSTOM_CODE 0x1fe /* ANT DTM V3 Remote Control */ /* Key Table */ #define KEY_MENU 0xB6 #define KEY_INFO 0xB9 #define KEY_EXIT 0xD3 #define KEY_LIB 0xC9 #define KEY_OK 0xC3 #define KEY_STOP 0xC6 #define KEY_RED 0xE1 #define KEY_GREEN 0xE2 #define KEY_YELLOW 0xE3 #define KEY_BLUE 0xE4 #define KEY_BACK 0xD2 #define KEY_VOLUP 0xD4 #define KEY_VOLDOWN 0xD7 #define KEY_PUP 0xD6 #define KEY_PDOWN 0xD9 #define KEY_RWND 0xBE #define KEY_FFWD 0xBF #define KEY_GUIDE 0xB8 #define KEY_PAUSE 0xC7 #define KEY_ONE 0xA1 #define KEY_TWO 0xA2 #define KEY_THREE 0xA3 #define KEY_FOUR 0xA4 #define KEY_FIVE 0xA5 #define KEY_SIX 0xA6 #define KEY_SEVEN 0xA7 #define KEY_EIGHT 0xA8 #define KEY_NINE 0xA9 #define KEY_ZERO 0xA0 #define SYSCLOCK 16000000 // main Arduino clock #define NEC_HDR_MARK 9000 #define NEC_HDR_SPACE 4500 #define NEC_BIT_MARK 560 #define NEC_ONE_SPACE 1600 #define NEC_ZERO_SPACE 560 #define TOPBIT 0x80000000 void Mark(int time); void Space(int time); void enableIROut(int khz); void sendNEC(unsigned long data, int nbits); int IR_EnCode(int custom_code, byte key_code, byte *converted_codes); void IR_Send(int custom_code, byte *converted_codes); #if defined(FLASH_LED) int ledPin = 13; // LED connected to digital pin 13 #endif typedef struct { byte key; int wait; } KeySequence; #define MAX_SEQ 60 KeySequence key_sequences[MAX_SEQ]; int max_counter = -1; int key_counter = 0; void setup() { int i; /* Setup the key sequences. * Here we will send 10 channel Ups, followed by 10 channel downs. * with a delay of 4 seconds between each key press. */ for (i = 0; i < 10; i++) { key_sequences[i].key = KEY_PUP; key_sequences[i].wait = 4000; } for (i = 10; i < 20; i++) { key_sequences[i].key = KEY_PDOWN; key_sequences[i].wait = 4000; } max_counter = 20; Serial.begin(115200); #if defined(FLASH_LED) pinMode(ledPin, OUTPUT); digitalWrite(ledPin, LOW); #endif delay(5000); } void loop() { #if defined(FLASH_LED) digitalWrite(ledPin, HIGH); #endif byte converted_codes[2] = { 0x00, 0x00}; if (0 == IR_EnCode(CUSTOM_CODE, key_sequences[key_counter].key, converted_codes)) { Serial.print("Send index "); Serial.println(key_counter); IR_Send(CUSTOM_CODE, converted_codes); } #if defined(FLASH_LED) digitalWrite(ledPin, LOW); #endif delay(key_sequences[key_counter].wait); ++key_counter; if (key_counter >= max_counter) key_counter = 0; } void enableIROut(int khz) { // Enables IR output. The khz value controls the modulation frequency in kilohertz. // The IR output will be on pin 3 (OC2B). // This routine is designed for 36-40KHz; if you use it for other values, it's up to you // to make sure it gives reasonable results. (Watch out for overflow / underflow / rounding.) // TIMER2 is used in phase-correct PWM mode, with OCR2A controlling the frequency and OCR2B // controlling the duty cycle. // There is no prescaling, so the output frequency is 16MHz / (2 * OCR2A) // To turn the output on and off, we leave the PWM running, but connect and disconnect the output pin. // A few hours staring at the ATmega documentation and this will all make sense. // See my Secrets of Arduino PWM at http://arcfn.com/2009/07/secrets-of-arduino-pwm.html for details. // Disable the Timer2 Interrupt (which is used for receiving IR) TIMSK2 &= ~_BV(TOIE2); //Timer2 Overflow Interrupt pinMode(3, OUTPUT); digitalWrite(3, LOW); // When not sending PWM, we want it low // COM2A = 00: disconnect OC2A // COM2B = 00: disconnect OC2B; to send signal set to 10: OC2B non-inverted // WGM2 = 101: phase-correct PWM with OCRA as top // CS2 = 000: no prescaling TCCR2A = _BV(WGM20); TCCR2B = _BV(WGM22) | _BV(CS20); // The top value for the timer. The modulation frequency will be SYSCLOCK / 2 / OCR2A. OCR2A = SYSCLOCK / 2 / khz / 1000; OCR2B = OCR2A / 3; // 33% duty cycle } void sendNEC(unsigned long data, int nbits) { enableIROut(38); Mark(NEC_HDR_MARK); Space(NEC_HDR_SPACE); for (int i = 0; i < nbits; i++) { if (data & TOPBIT) { Mark(NEC_BIT_MARK); Space(NEC_ONE_SPACE); } else { Mark(NEC_BIT_MARK); Space(NEC_ZERO_SPACE); } data <<= 1; } Mark(NEC_BIT_MARK); Space(0); } void Mark(int time) { // Sends an IR mark for the specified number of microseconds. // The mark output is modulated at the PWM frequency. TCCR2A |= _BV(COM2B1); // Enable pin 3 PWM output delayMicroseconds(time); } /* Leave pin off for time (given in microseconds) */ void Space(int time) { // Sends an IR space for the specified number of microseconds. // A space is no output, so the PWM output is disabled. TCCR2A &= ~(_BV(COM2B1)); // Disable pin 3 PWM output delayMicroseconds(time); } int IR_EnCode(int custom_code, byte key_code, byte *converted_codes) { int i; #if defined(DEBUG) Serial.print("IR_Transmit, using Custom Code: "); Serial.print(custom_code, HEX); Serial.print(" And KeyCode "); Serial.println(key_code, HEX); #endif for (i = 0; i < 8; i++) converted_codes[0] += ((key_code>>i)&1)<<(7-i); for (i = 0; i < 8; i++) { if (((converted_codes[0] >> (7-i))&1) == 0) converted_codes[1] += 1; if (i != 7) converted_codes[1] <<= 1; } #if defined(DEBUG) Serial.print("Converted Key Code = "); Serial.print(converted_codes[0], HEX); Serial.print(" and "); Serial.println(converted_codes[1], HEX); #endif return 0; } void IR_Send(int custom_code, byte *converted_codes) { unsigned long data; /* Build up the 32 bits of data */ data = custom_code; data <<=8; data |= converted_codes[0]; data <<=8; data |= converted_codes[1]; sendNEC(data, 32); }