IR transmitter & recorder

I would like some small box that sits ...

You can put this type of device together yourself using Spark or arduino type systems. An of course there are some available to buy off the shelf (via google & $$$)

Definitely looking to do it with a Spark - itā€™s basically just another use for the original request.

@BDub - Sorry for the delay Sh*t hit the fan my end unfortunately & havenā€™t been able to make any progress on this in the meantime.

Just sat down and sent your code to the spark & tried to record the signal with analysIRā€¦ which is picking up a signal, but Iā€™m not sure it is fast enough to track as a recorded pattern?

The data table looks like this though:

1 0 1000 0
2 1000 40248 1
3 41248 9704 0
4 50952 40240 1
5 91192 9704 0
6 100896 40244 1
7 141140 9704 0
8 150844 40240 1
9 191084 9672 0
10 200756 40272 1
11 241028 9672 0
12 250700 40276 1
13 290976 9672 0
14 300648 40244 1
15 340892 9700 0
16 350592 40248 1
17 390840 9704 0
18 400544 40240 1
19 440784 9704 0
20 450488 40244 1
21 490732 9704 0
22 500436 40240 1
23 540676 9704 0
24 550380 40244 1
25 590624 9704 0
26 600328 40248 1
27 640576 20324 0
28 660900 10084 1
29 670984 19144 0
30 690128 10100 1
31 700228 19868 0
32 720096 10096 1
33 730192 19872 0
34 750064 10100 1
35 760164 19868 0
36 780032 10096 1
37 790128 19872 0
38 810000 10096 1
39 820096 19872 0
40 839968 10096 1
41 850064 19872 0
42 869936 10096 1
43 880032 19872 0
44 899904 10096 1
45 910000 29860 0
46 939860 40272 1
47 980132 9676 0
48 989808 40272 1
49 1030080 9672 0
50 1039752 40272 1

Why have you switched back to using delay rather than delay microseconds? - Iā€™m not sure what the output is supposed to look like unfortunately so canā€™t really diagnose whats going on with it :confused:

Because I didn't have any idea what the modulation timing would be, so I just picked milliseconds based on some timing of your original code.

Your AnalysIR looks to be decoding 40ms on as 1 and 10ms on as 0. That means nothing though because it's just some made up on/off timing that I randomly decided to test out my code.

Based on what @AnalysIR said, if you are trying to emulate a SKY remote with RC6 varient coding, you probably should change the routine to interpret the input ON/OFF times as microseconds, rather than milliseconds, and use delayMicroseconds() everywhere.

You basically have to break down the IR code you are trying to emulate, and then think about how to recreate the smallest pieces first, then the next level up from that and so on.

Yes, it looks like @BDub 's code will work given the faster CPU speed of the Spark. You only have to convert his functions to take uSecs instead of ms and as He said just adjust for your SKY codes.

Longer term it would be better to use onboard PWM @33% duty cycle, instead of a decicated (blocking) function.

Here is an image of the generated signal which I was able to import from @bedsingar 's export of timings.

FYI: here are some the HEX representations of SKY HD codes & SKY+ codes from a project I was working on. I believe the SKY HD devices will also work with SKY+ codes but not vice-versa.

/*
//IR Codes SKY HD
 #define UP 0xC05C21
 #define Down 0xC05C21
 #define CHUP 0xC05C20
 #define CHDOWN 0xC05C21
 #define VOLUP 0xC05C21
 #define VOLDOWN 0xC05C21
 #define Menu 0xC05C21
 
 #define Back 0xC05C21
 #define Right 0xC05C21
 #define Left 0xC05C21
 #define Play 0xC05C21
 #define OK 0xC05C0C
 */

/*
//IR Codes SKY Plus
//0-9
#define NUM0 0xC00C00
#define NUM1 0xC00C01
#define NUM2 0xC00C02
#define NUM3 0xC00C03
#define NUM4 0xC00C04
#define NUM5 0xC00C05
#define NUM6 0xC00C06
#define NUM7 0xC00C07
#define NUM8 0xC00C08
#define NUM9 0xC00C09

#define UP 0xC00C58
#define DOWN 0xC00C59
#define CHUP 0xC00C20
#define CHDOWN 0xC00C21
#define TVGUIDE 0xC00CCC
#define SERVICES 0xC00C7E
#define BOXOFFICE 0xC00C7D
#define INTERACTIVE 0xC00CF5
#define RIGHT 0xC00C5B
#define LEFT 0xC00C5A
*/

All you need to do is the set up the RC6 headers and then send 'ā€˜onesā€™ and ā€˜zerosā€™ based on bitshifting these HEX values. There is also a toggle bit that needs to be implemented as well.

Currently I can set the PWM to a custom frequency up to 64kHz (probably higher). So basically you just set it to the carrier frequency and then modulate it 0% duty cycle / 33% duty cycle at the desired rate? Technically we are still blocking if we do that, and unfortunately with the way things are right now we canā€™t let the main loop run while processing IR codes because the background tasks take 5-6ms.

Just for reference, the modulation frequency for RC6 / SKY is 36kHz.

99.99% of IR remotes use a modulation frequency in the range of 30kHz to 56kHz. There is one from Bang & Olufsen that modulates @ 455kHz, but it is very rare and the IR receivers are even harder to get, so essentially can be forgotten/omitted.

@BDub , @bedsingar

I decided to come at this from a different angle (or tried to not reinvent the wheel) ā€¦
I loaded the arduino IRremote library to my arduino uno & got that controlling my TV within a few hours. Nice to see its within reach. Iā€™m in a differnt house to where I started the project so donā€™t have a sky box handy to test on and have today been playing with SAMSUNG remote codes in RAW format.

I thought Iā€™d give porting the libraries over to the Spark a go - although I donā€™t really know what Iā€™m doing I figured that I should be able to copy the functions over from all of the included files - omitting anything that looked related to the receiver rather than the transmitter.

Then started verifying the code & debugging on a trial and error basis one issue at a time.

Iā€™m down to only a few errors on compile (although donā€™t know if it will work once done any way!) ā€¦ but some of the errors I have seem to be related to PWM functionality - I just thought Iā€™d ask if the core has any limitations that are going to stop this from working?

Currently the issues are:

the_user_app.cpp: In member function 'void IRsend::mark(int)':
the_user_app.cpp:394:3: error: 'TCCR2A' was not declared in this scope
the_user_app.cpp:394:3: error: 'COM2B1' was not declared in this scope
the_user_app.cpp:394:3: error: 'BV' was not declared in this scope
theuser_app.cpp: In member function 'void IRsend::space(int)':
the_user_app.cpp:402:3: error: 'TCCR2A' was not declared in this scope
the_user_app.cpp:402:3: error: 'COM2B1' was not declared in this scope
the_user_app.cpp:402:3: error: 'BV' was not declared in this scope
theuser_app.cpp: In member function 'void IRsend::enableIROut(int)':
the_user_app.cpp:420:3: error: 'TIMSK2' was not declared in this scope
the_user_app.cpp:423:34: error: call of overloaded 'digitalWrite(int, int)' is ambiguous
the_user_app.cpp:423:34: note: candidates are:
In file included from ../inc/application.h:29:0,
from the_user_app.cpp:6:
../inc/spark_wiring.h:155:6: note: void digitalWrite(uint16_t, uint8_t)
the_user_app.cpp:92:6: note: void digitalWrite(uint8_t, uint8_t)
the_user_app.cpp:430:3: error: 'TCCR2A' was not declared in this scope
the_user_app.cpp:430:3: error: 'WGM20' was not declared in this scope
the_user_app.cpp:430:3: error: 'BV' was not declared in this scope
theuser_app.cpp:430:3: error: 'TCCR2B' was not declared in this scope
the_user_app.cpp:430:3: error: 'WGM22' was not declared in this scope
the_user_app.cpp:430:3: error: 'CS20' was not declared in this scope
the_user_app.cpp:430:3: error: 'OCR2A' was not declared in this scope
the_user_app.cpp:430:3: error: 'OCR2B' was not declared in this scope
the_user_app.cpp: At global scope:
the_user_app.cpp:442:4: error: expected constructor, destructor, or type conversion before '(' token
make: *** [the_user_app.o] Error 1
//taken from Arduino.h --------------------------------------------------------------------------------------------------
ifdef __cplusplus
extern "C"{

endif
define HIGH 0x1
define LOW 0x0
define INPUT 0x0
define OUTPUT 0x1
define INPUT_PULLUP 0x2
define true 0x1
define false 0x0
define PI 3.1415926535897932384626433832795
define HALF_PI 1.5707963267948966192313216916398
define TWO_PI 6.283185307179586476925286766559
define DEG_TO_RAD 0.017453292519943295769236907684886
define RAD_TO_DEG 57.295779513082320876798154814105
define SERIAL 0x0
define DISPLAY 0x1
define LSBFIRST 0
define MSBFIRST 1
define CHANGE 1
define FALLING 2
define RISING 3
if defined(AVR_ATtiny24) || defined(AVR_ATtiny44) || defined(AVR_ATtiny84) || defined(AVR_ATtiny25) || defined(AVR_ATtiny45) || defined(AVR_ATtiny85)
define DEFAULT 0
define EXTERNAL 1
define INTERNAL 2
else
if defined(AVR_ATmega1280) || defined(AVR_ATmega2560) || defined(AVR_ATmega1284) || defined(AVR_ATmega1284P) || defined(AVR_ATmega644) || defined(AVR_ATmega644A) || defined(AVR_ATmega644P) || defined(AVR_ATmega644PA)
define INTERNAL1V1 2
define INTERNAL2V56 3
else
define INTERNAL 3
endif
define DEFAULT 1
define EXTERNAL 0
endif
// undefine stdlib's abs if encountered

ifdef abs
undef abs
endif
define min(a,b) ((a)<(b)?(a)frowningb))
define max(a,b) ((a)>(b)?(a)frowningb))
define abs(x) ((x)>0?(x)frowningx))
define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high)frowningamt)))
define round(x) ((x)>=0?(long)((x)+0.5)frowninglong)((x)-0.5))
define radians(deg) ((deg)*DEG_TO_RAD)
define degrees(rad) ((rad)*RAD_TO_DEG)
define sq(x) ((x)*(x))
define interrupts() sei()
define noInterrupts() cli()
define clockCyclesPerMicrosecond() ( F_CPU / 1000000L )
define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() )
define microsecondsToClockCycles(a) ( (a) * clockCyclesPerMicrosecond() )
define lowByte(w) ((uint8_t) ((w) & 0xff))
define highByte(w) ((uint8_t) ((w) >> 8))
define bitRead(value, bit) (((value) >> (bit)) & 0x01)
define bitSet(value, bit) ((value) |= (1UL << (bit)))
define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
define bitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit) : bitClear(value, bit))
typedef unsigned int word;

define bit(b) (1UL << (b))
//typedef uint8_t boolean;
typedef uint8_t byte;

void init(void);

void pinMode(uint8_t, uint8_t);
void digitalWrite(uint8_t, uint8_t);
int digitalRead(uint8_t);
int analogRead(uint8_t);
void analogReference(uint8_t mode);
void analogWrite(uint8_t, int);

//unsigned long millis(void);
//unsigned long micros(void);
//void delay(unsigned long);
//void delayMicroseconds(unsigned int us);
unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout);

//void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val);
//uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder);

void attachInterrupt(uint8_t, void (*)(void), int mode);
void detachInterrupt(uint8_t);

void setup(void);
void loop(void);

// Get the bit location within the hardware port of the given virtual pin.
// This comes from the pins_*.c file for the active board configuration.

define analogInPinToBit(P) (P)
// On the ATmega1280, the addresses of some of the port registers are
// greater than 255, so we can't store them in uint8_t's.
//extern const uint16_t PROGMEM port_to_mode_PGM[];
//extern const uint16_t PROGMEM port_to_input_PGM[];
//extern const uint16_t PROGMEM port_to_output_PGM[];

//extern const uint8_t PROGMEM digital_pin_to_port_PGM[];
// extern const uint8_t PROGMEM digital_pin_to_bit_PGM[];
//extern const uint8_t PROGMEM digital_pin_to_bit_mask_PGM[];
//extern const uint8_t PROGMEM digital_pin_to_timer_PGM[];

// Get the bit location within the hardware port of the given virtual pin.
// This comes from the pins_*.c file for the active board configuration.
// 
// These perform slightly better as macros compared to inline functions
//

define digitalPinToPort(P) ( pgm_read_byte( digital_pin_to_port_PGM + (P) ) )
define digitalPinToBitMask(P) ( pgm_read_byte( digital_pin_to_bit_mask_PGM + (P) ) )
define digitalPinToTimer(P) ( pgm_read_byte( digital_pin_to_timer_PGM + (P) ) )
define analogInPinToBit(P) (P)
define portOutputRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_output_PGM + (P))) )
define portInputRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_input_PGM + (P))) )
define portModeRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_mode_PGM + (P))) )
define NOT_A_PIN 0
define NOT_A_PORT 0
ifdef ARDUINO_MAIN
define PA 1
define PB 2
define PC 3
define PD 4
define PE 5
define PF 6
define PG 7
define PH 8
define PJ 10
define PK 11
define PL 12
endif
define NOT_ON_TIMER 0
define TIMER0A 1
define TIMER0B 2
define TIMER1A 3
define TIMER1B 4
define TIMER2 5
define TIMER2A 6
define TIMER2B 7
define TIMER3A 8
define TIMER3B 9
define TIMER3C 10
define TIMER4A 11
define TIMER4B 12
define TIMER4C 13
define TIMER4D 14
define TIMER5A 15
define TIMER5B 16
define TIMER5C 17
ifdef __cplusplus
} // extern "C"

endif
ifdef __cplusplus
uint16_t makeWord(uint16_t w);
uint16_t makeWord(byte h, byte l);

define word(...) makeWord(VA_ARGS)
unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L);

void tone(uint8_t pin, unsigned int frequency, unsigned long duration = 0);
void noTone(uint8t _pin);

// WMath prototypes
long random(long);
long random(long, long);
void randomSeed(unsigned int);
long map(long, long, long, long, long);

endif
//#endif
//TAKEN FROM IRremote.h --------------------------------------------------------------------------------------------------

ifndef IRremote_h
define IRremote_h
class IRsend
{
public:
IRsend() {}

void sendRaw(unsigned int buf[], int len, int hz);
// private:
void enableIROut(int khz);
void mark(int usec);
void space(int usec);
}
;

// Some useful constants
//0

define USECPERTICK 50 // microseconds per clock interrupt tick
define RAWBUF 100 // Length of raw duration buffer
// Marks tend to be 100us too long, and spaces 100us too short
// when received due to sensor lag.

define MARK_EXCESS 100
endif
//TAKEN FROM IRREMOTEINT.h---------------------------------------------------------------------------------------------------------------

ifndef IRremoteint_h
define IRremoteint_h
//#if defined(ARDUINO) && ARDUINO >= 100
//#include 
//#else
//#include 
//#endif

// define which timer to use
//
// Uncomment the timer you wish to use on your board. If you
// are using another library which uses timer2, you have options
// to switch IRremote to use a different timer.

#define IR_USE_TIMER2 // tx = pin 9

ifdef F_CPU
define SYSCLOCK F_CPU // main Arduino clock
else
define SYSCLOCK 16000000 // main Arduino clock
endif
define ERR 0
define DECODED 1
// defines for setting and clearing register bits

ifndef cbi
define cbi(sfr, bit) (SFRBYTE(sfr) &= ~_BV(bit))
endif
ifndef sbi
define sbi(sfr, bit) (SFRBYTE(sfr) |= _BV(bit))
endif
// Pulse parms are 50-100 for the Mark and 50+100 for the space
// First MARK is the one after the long gap
// pulse parameters in usec

define TOLERANCE 25 // percent tolerance in measurements
define LTOL (1.0 - TOLERANCE/100.)
define UTOL (1.0 + TOLERANCE/100.)
define _GAP 5000 // Minimum map between transmissions
define GAP_TICKS (_GAP/USECPERTICK)
define TICKS_LOW(us) (int) (((us)*LTOL/USECPERTICK))
define TICKS_HIGH(us) (int) (((us)*UTOL/USECPERTICK + 1))
ifndef DEBUG
int MATCH(int measured, int desired) {return measured >= TICKS_LOW(desired) && measured <= TICKS_HIGH(desired);}
int MATCH_MARK(int measured_ticks, int desired_us) {return MATCH(measured_ticks, (desired_us + MARK_EXCESS));}
int MATCH_SPACE(int measured_ticks, int desired_us) {return MATCH(measured_ticks, (desired_us - MARK_EXCESS));}
// Debugging versions are in IRremote.cpp

endif
// information for the interrupt handler
typedef struct {
uint8_t recvpin; // pin for IR data from detector
uint8_t rcvstate; // state machine
uint8_t blinkflag; // TRUE to enable blinking of pin 13 on IR processing
unsigned int timer; // state timer, counts 50uS ticks.
unsigned int rawbuf[RAWBUF]; // raw data
uint8_t rawlen; // counter of entries in rawbuf
} 
irparams_t;

// Defined in IRremote.cpp
extern volatile irparams_t irparams;

// IR detector output is active low

define MARK 0
define SPACE 1
define TOPBIT 0x80000000
// defines for timer2 (8 bits)

if defined(IR_USE_TIMER2)
define TIMER_RESET
define TIMER_ENABLE_PWM (TCCR2A |= _BV(COM2B1))
define TIMER_DISABLE_PWM (TCCR2A &= ~(_BV(COM2B1)))
define TIMER_ENABLE_INTR (TIMSK2 = _BV(OCIE2A))
define TIMER_DISABLE_INTR (TIMSK2 = 0)
define TIMER_INTR_NAME TIMER2_COMPA_vect
define TIMER_CONFIG_KHZ(val) ({ \
const uint8_t pwmval = SYSCLOCK / 2000 / (val); \
TCCR2A = BV(WGM20); \
TCCR2B = BV(WGM22) | _BV(CS20); \
OCR2A = pwmval; \
OCR2B = pwmval / 3; \
})

define TIMER_COUNT_TOP (SYSCLOCK * USECPERTICK / 1000000)
if (TIMER_COUNT_TOP < 256)
define TIMER_CONFIG_NORMAL() ({ \
TCCR2A = BV(WGM21); \
TCCR2B = BV(CS20); \
OCR2A = TIMER_COUNT_TOP; \
TCNT2 = 0; \
})

else
define TIMER_CONFIG_NORMAL() ({ \
TCCR2A = BV(WGM21); \
TCCR2B = BV(CS21); \
OCR2A = TIMER_COUNT_TOP / 8; \
TCNT2 = 0; \
})

endif
if defined(CORE_OC2B_PIN)
define TIMER_PWM_PIN CORE_OC2B_PIN /* Teensy */
elif defined(AVR_ATmega1280) || defined(AVR_ATmega2560)
define TIMER_PWM_PIN 9 /* Arduino Mega */
elif defined(AVR_ATmega644P) || defined(AVR_ATmega644)
define TIMER_PWM_PIN 14 /* Sanguino */
else
define TIMER_PWM_PIN 3 /* Arduino Duemilanove, Diecimila, LilyPad, etc */
endif
else // unknown timer
error "Internal code configuration error, no known IR_USE_TIMER# defined\n"
endif
endif
//TAKEN FROM IRREMOTE.cpp -------------------------------------------------------------------------------------------------

volatile irparams_t irparams;

void IRsend::sendRaw(unsigned int buf[], int len, int hz)
{
enableIROut(hz);
for (int i = 0; i < len; i++) {
if (i & 1) {
space(buf[i]);
} 
else {
mark(buf[i]);
}
}
space(0); // Just to be sure
}

void IRsend::mark(int time) {
// Sends an IR mark for the specified number of microseconds.
// The mark output is modulated at the PWM frequency.
TIMER_ENABLE_PWM; // Enable pin 3 PWM output
delayMicroseconds(time);
}

/* Leave pin off for time (given in microseconds) */
void IRsend::space(int time) {
// Sends an IR space for the specified number of microseconds.
// A space is no output, so the PWM output is disabled.
TIMER_DISABLE_PWM; // Disable pin 3 PWM output
delayMicroseconds(time);
}

void IRsend::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)
TIMER_DISABLE_INTR; //Timer2 Overflow Interrupt

pinMode(TIMER_PWM_PIN, OUTPUT);
digitalWrite(TIMER_PWM_PIN, 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
// The top value for the timer. The modulation frequency will be SYSCLOCK / 2 / OCR2A.
TIMER_CONFIG_KHZ(khz);
}

// TIMER2 interrupt code to collect raw data.
// Widths of alternating SPACE, MARK are recorded in rawbuf.
// Recorded in ticks of 50 microseconds.
// rawlen counts the number of entries recorded so far.
// First entry is the SPACE between transmissions.
// As soon as a SPACE gets long, ready is set, state switches to IDLE, timing of SPACE continues.
// As soon as first MARK arrives, gap width is recorded, ready is cleared, and new logging starts
ISR(TIMER_INTR_NAME)
{
TIMER_RESET;

uint8_t irdata = (uint8_t)digitalRead(irparams.recvpin);

irparams.timer++; // One more 50us tick
if (irparams.rawlen >= RAWBUF) {
// Buffer overflow
irparams.rcvstate = STATE_STOP;
}
switch(irparams.rcvstate) {
case STATE_IDLE: // In the middle of a gap
if (irdata == MARK) {
if (irparams.timer < GAP_TICKS) {
// Not big enough to be a gap.
irparams.timer = 0;
} 
else {
// gap just ended, record duration and start recording transmission
irparams.rawlen = 0;
irparams.rawbuf[irparams.rawlen++] = irparams.timer;
irparams.timer = 0;
irparams.rcvstate = STATE_MARK;
}
}
break;
case STATE_MARK: // timing MARK
if (irdata == SPACE) { // MARK ended, record time
irparams.rawbuf[irparams.rawlen++] = irparams.timer;
irparams.timer = 0;
irparams.rcvstate = STATE_SPACE;
}
break;
case STATE_SPACE: // timing SPACE
if (irdata == MARK) { // SPACE just ended, record it
irparams.rawbuf[irparams.rawlen++] = irparams.timer;
irparams.timer = 0;
irparams.rcvstate = STATE_MARK;
} 
else { // SPACE
if (irparams.timer > GAP_TICKS) {
// big SPACE, indicates gap between codes
// Mark current code as ready for processing
// Switch to STOP
// Don't reset timer; keep counting space width
irparams.rcvstate = STATE_STOP;
} 
}
break;
case STATE_STOP: // waiting, measuring gap
if (irdata == MARK) { // reset gap timer
irparams.timer = 0;
}
break;
}

if (irparams.blinkflag) {
if (irdata == MARK) {
BLINKLED_ON(); // turn pin 13 LED on
} 
else {
BLINKLED_OFF(); // turn pin 13 LED off
}
}
}

// Actual Code from ino file -------------------------------------------------------------------------------------------------------

IRsend irsend;
char incomingByte = 'a'; // for incoming serial data

void setup()
{
Serial.begin(9600);
}

// Power ON/OFF
unsigned int S_pwr[68]={4600,4350,700,1550,650,1550,650,1600,650,450,650,450,650,450,650,450,700,400,700,1550,650,1550,650,1600,650,450,650,450,650,450,700,450,650,450,650,450,650,1550,700,450,650,450,650,450,650,450,650,450,700,400,650,1600,650,450,650,1550,650,1600,650,1550,650,1550,700,1550,650,1550,650};
// channel 1 
unsigned int S_1[68]={4650,4300,700,1550,700,1550,650,1550,700,400,700,400,700,400,700,450,700,400,700,1500,700,1500,700,1550,700,450,650,400,700,450,650,450,700,400,700,400,700,450,650,1550,700,400,700,400,700,400,700,450,650,450,650,1550,700,1500,700,450,650,1550,700,1550,650,1550,700,1500,700,1550,650};
// channel 2
unsigned int S_2[68]={4600,4350,650,1550,700,1500,700,1550,700,400,700,400,700,450,650,450,700,400,700,1500,700,1500,700,1550,700,400,700,450,650,450,700,400,700,400,700,1500,700,400,700,1550,700,400,700,400,700,450,650,450,700,400,700,400,700,1550,650,450,700,1500,700,1550,650,1550,700,1500,700,1550,650};
// channel 3
unsigned int S_3[68]={4600,4350,700,1500,700,1550,650,1600,650,400,700,450,700,400,700,400,700,400,700,1550,650,1550,700,1500,700,400,700,450,700,400,700,400,700,400,700,400,700,1550,700,1500,700,450,650,450,700,400,700,400,700,400,700,1550,700,400,700,400,700,1550,650,1550,700,1500,700,1550,700,1500,700};
// channel 4
unsigned int S_4[68]={4600,4350,650,1550,700,1500,700,1550,700,400,700,400,700,450,650,450,700,400,700,1500,700,1550,650,1550,700,400,700,450,650,450,700,400,700,400,700,400,700,400,700,450,650,1550,700,400,700,400,700,450,700,400,700,1500,700,1550,650,1550,700,400,700,1550,650,1550,700,1500,700,1550,650};
// channel 5
unsigned int S_5[68]={4650,4350,700,1500,700,1550,650,1550,700,400,700,450,700,400,700,400,700,400,700,1500,700,1550,700,1500,700,450,650,450,700,400,700,400,700,400,700,1550,700,400,700,400,650,1550,700,450,650,450,700,400,700,450,650,450,650,1550,650,1550,700,400,700,1550,700,1500,700,1500,700,1550,700};
// channel 6
unsigned int S_6[68]={4600,4350,650,1550,700,1500,700,1550,700,400,700,400,700,450,650,450,700,400,700,1500,700,1550,650,1550,700,400,700,450,700,400,700,400,700,400,700,400,700,1550,700,400,700,1500,700,450,650,450,700,400,700,400,700,1550,650,450,650,1550,700,400,700,1550,650,1550,700,1500,700,1550,650};
// channel 7
unsigned int S_7[68]={4600,4350,700,1500,700,1550,650,1550,700,400,700,450,700,400,700,400,700,400,700,1550,650,1550,700,1500,700,400,700,450,700,400,700,400,700,400,700,450,650,450,650,1550,700,1500,700,450,700,400,700,400,700,450,650,1550,650,1550,700,450,650,400,700,1550,700,1500,700,1550,650,1550,700};
// channel 8
unsigned int S_8[68]={4600,4350,650,1600,650,1500,700,1550,700,400,700,400,700,400,700,450,700,400,700,1500,700,1550,650,1550,700,400,700,450,650,450,700,400,700,400,700,1550,650,450,650,1550,700,1500,700,450,700,400,700,400,700,400,700,400,700,1550,700,400,700,450,650,1550,650,1550,700,1500,700,1550,650};
// channel 9
unsigned int S_9[68]={4600,4350,700,1500,700,1550,650,1550,700,400,700,450,650,450,650,450,700,400,700,1500,700,1550,700,1550,650,400,700,450,700,400,700,400,700,400,700,450,650,1550,650,1600,650,1550,650,450,700,400,700,400,700,400,700,1550,700,400,700,400,700,400,700,1550,700,1500,700,1500,700,1550,700};
// channel 0
unsigned int S_0[68]={4650,4300,700,1550,700,1500,700,1550,700,400,700,400,700,400,700,450,650,450,650,1550,700,1550,650,1550,700,400,700,400,700,400,700,450,700,400,700,1550,650,400,700,450,700,400,650,1550,700,400,700,450,700,400,700,400,700,1500,700,1550,700,1500,700,400,700,1550,650,1550,700,1500,700};
// source
unsigned int S_scr[68]={4600,4350,700,1550,650,1550,700,1500,700,450,650,450,700,400,700,400,700,400,700,1550,700,1500,700,1550,700,400,700,400,700,400,700,400,700,400,700,1550,700,400,700,450,650,450,650,450,700,400,700,400,700,400,700,450,650,1550,700,1500,700,1550,650,1550,700,1500,700,1550,700,1500,700};
// channel up
unsigned int S_pup[68]={4600,4350,700,1500,700,1500,700,1550,700,450,650,400,700,450,650,450,700,400,700,1500,700,1550,650,1550,700,450,650,450,700,400,700,400,700,400,700,400,700,1550,700,400,700,400,700,1550,650,450,700,400,700,400,700,1550,650,450,650,1600,650,1550,650,450,700,1500,700,1500,700,1550,650};
// channel down
unsigned int S_pdown[68]={4650,4300,700,1550,700,1500,700,1550,700,400,700,400,700,400,700,450,650,450,650,1550,700,1500,700,1550,700,400,700,400,700,400,700,450,700,400,700,400,700,400,700,450,650,450,650,1550,700,400,700,450,650,400,700,1550,700,1500,700,1550,700,1500,700,400,700,1550,650,1550,700,1500,700};
// volume up
unsigned int S_vup[68]={4600,4350,650,1550,700,1500,700,1550,700,400,700,400,700,450,650,450,700,400,700,1500,700,1550,650,1550,700,400,700,400,700,450,650,450,700,400,700,1500,700,1550,650,1550,700,400,700,450,700,400,700,400,700,400,700,450,650,450,650,450,650,1550,700,1500,700,1550,700,1500,700,1550,650};
// volume down
unsigned int S_vdown[68]={4600,4350,700,1550,650,1550,700,1500,700,450,650,450,700,400,700,400,700,400,700,1550,700,1500,700,1550,700,400,700,400,700,400,700,450,650,450,650,1550,700,1500,700,450,650,1550,700,400,700,400,700,450,700,400,700,400,700,400,700,1550,700,400,700,1500,700,1500,700,1550,700,1500,700};
// TV/DTV
unsigned int S_tv[68]={4600,4350,650,1550,700,1500,700,1550,700,400,700,400,700,400,700,450,700,400,700,1500,700,1500,700,1550,700,400,700,400,700,450,650,450,700,400,700,1500,700,1550,700,400,700,400,700,400,700,400,700,1550,700,400,700,400,700,400,700,1550,700,1500,700,1550,650,1550,700,400,700,1500,700};
// guide
unsigned int S_guide[68]={4600,4350,700,1500,700,1550,700,1500,700,450,650,450,700,400,700,400,700,400,700,1550,650,1550,700,1500,700,450,650,450,700,400,700,400,700,400,700,1550,700,1500,700,1550,650,1550,700,400,700,400,700,1550,700,400,700,400,700,400,700,450,700,400,650,1550,700,1550,650,450,700,1500,700};
// exit
unsigned int S_exit[68]={4650,4300,700,1550,650,1550,700,1550,700,400,700,400,700,450,650,450,650,450,650,1550,700,1500,700,1550,700,450,650,400,700,450,650,450,700,400,700,1500,700,400,700,1550,700,1500,700,400,700,1550,700,450,650,400,700,450,650,1550,700,400,700,400,700,1550,650,450,650,1550,700,1500,700};
// mute
unsigned int S_mute[68]={4650,4350,650,1550,650,1550,700,1550,700,400,700,400,700,400,700,450,650,450,650,1550,700,1500,700,1550,700,400,700,450,650,400,700,450,700,400,700,1500,700,1550,650,1550,700,1500,700,450,700,400,700,400,700,400,700,400,700,450,650,450,700,400,700,1500,700,1550,650,1550,700,1500,700};

void loop() {

if (Serial.read() != -1) {
Serial.print("loop ");

// read the incoming byte:
            incomingByte = Serial.read();

            // say what you got:
            Serial.print("I received: ");
            Serial.println(incomingByte, HEX);

for (int i = 0; i < 3; i++) {
        irsend.sendRaw(S_pwr,68,38);    
  delay(100);
}
}

}

The files I havenā€™t copied in yet are:

include stdlib.h #include string.h #include math.h #include avr/pgmspace.h #include avr/io.h #include avr/interrupt.h #include binary.h #include WCharacter.h #include WString.h #include HardwareSerial.h #include pins_arduino.h

Because I think they may already exist on the core by default?

Hopefully it wonā€™t be too difficult to complete this, then I can start working on calling different commands with the API.

Thanks

Most of the issues you will find are:

  1. Sending: use of the timers on AVR for generating the PWM. I did a quick check on the spark docs and the only reference to PWM is via the analogue write function, which will not be sufficient. More low level access to the sparkā€™s PWM will be required for a full port. In the interim you can do a temporary hack in the mark & space functions to emulate PWM via the function supplied by @BDub. (and just disable all the Arduino PWM stuff initially)
  2. Receiving: Again this uses a 50 uSec clock granularity on the AVRsā€¦so shouldnā€™t be too difficult to port.
  3. Decoding: Once you have the above sorted, the decoding should be fine.

To check your progress, AnalysIR can import the RAW output from IRremote IRrecvDump example ā€¦looks like
Raw (42): -7328 100 -2900 100 -1750 100 -1100 100ā€¦ (Menu->File->Import)

Another thing you have to decide is whether to keep one source for just spark core or include spark core in the existing code.

There is also another library IRLib which is a more recent re-write of IRremote for Arduino. I donā€™t know, but it is possible that it may be easier to port from Arduino to Spark. I recently tried to get the Author to port it to Spark but he is busy porting to another platform.

1 Like

BTW, we do have low level access to PWM, see here: https://gist.github.com/technobly/8313449

ā€¦

1 Like

I have forked IRLib and made all the necessary changes to make it compile without errors on spark.
Itā€™s not working yet, I need to look at the timer interrupt more closely. Feel free to contribute or point me to example of working Timer IRQHandler functions

1 Like

Great

FYI: It may be easier to get the sending working first - If I am correct it only needs delayMicroseconds & PWM.

If you have a problem getting signals to send, you could try this basic script, which has proven useful recently on difficult signals that IRremote & IRLib cannot handle:
IR timings record script
(you may need to adjust for spark)

From those timings I can automatically generate the C code for IRLib or IRremote, with AnalysIR, so you can test it out.

The download link is at the bottom of the linked page.

I made a modified version of IRremote compatible with the Spark Core: https://github.com/qwertzguy/Spark-Core-IRremote
I used it successfully to control a Panansonic TV and a Roomba (using IR codes from https://gist.github.com/probonopd/5181021).

Please try it and tell me if it works successfully for you.

2 Likes

Oh myā€¦I just grabbed an IR Led yesterday hoping to control my fan via the webā€¦ :wink:

Where can I get the hex for the power on/off for my fan?

Sorry but I never tried IR before so Iā€™ll ask anything I dont know.

It usually hard. Google around and you might find it. Also check github search which sometimes finds better stuff than a general google search. Also if you find raw codes online, you can use sendRaw to send it. If you post the brand and model someone here might help you.

I saw from online:

`
The code is 56 bits, decoded into the following 7 bytes (in hexadecimal).

02 20 d0 84 30 31 55 ā€“ on/off
02 20 d0 84 30 21 45 ā€“ fan speed 1
02 20 d0 84 30 22 46 ā€“ fan speed 2
02 20 d0 84 30 23 47 ā€“ fan speed 3
02 20 d0 84 30 40 24 ā€“ timer cancel
02 20 d0 84 30 41 25 ā€“ timer 1hr
02 20 d0 84 30 43 27 ā€“ timer 3hr
02 20 d0 84 30 46 27 ā€“ timer 6hr
02 20 d0 84 30 4e 2a ā€“ sleep mod
`

Better if you get an IR receiver and measure the signals yourself, then use sendRaw. (see my previous post, which may help).

If you post the timings I can review the signal and let you know the best way to send it.

(Of course if you know the IR protocol the 56 bit sequences belong to, it may be a bit easier.

Thanks for the offer! Iā€™ll have to see if I can grab an IR receiver

Someone did it on Arduino here:

Trouble you to take a look at your free time :wink:

This IR thingy is giving me an idea to have it on my project to trigger stuff like smart configā€¦restart.etc when the system is in some enclosure!

Better than having to reach for the buttons!

@kennethlimcp you should be able to use the code you linked by combining with the lib posted by @qwertzguy.

You should be able to directly access the mark & space functions from IRremote using

irsend::mark(ā€¦); and irsend::space(ā€¦);