Sending IR codes in background

Ir-RxTx-groot.jpg

Hello guys.

After hours spent on the internet trying to understand how stuff works i came up with this piece of code. It simply sends out SONY remote codes, but the cool thing is that it does this in background, meaning that while your MCU sends out the code, you can carry on with your main loop, and you don't have to wait till the code has been sent to carry on with you sketch.

Even if this code sends out SONY codes, it can be easily adapted to send other protocols or even work at different frequencies and/or duty cycles (current is 50%). Here is the code...and please tell me if there's something you don't understand (i am willing to explain the different parts it's made of as soon as i have more time ;=) ).

Oh wait...i forgot to tell you how to use it. This is the function you're looking for: SonyIR(x, n). x is the code you want to send, while n is the number of bits it is made of. In the below example im sending it 3 times at a constant interval, because SONY "things" (tvs, recorders, etc..) only accept the code if repeated 3 times. You can use this code in combination with Ken Shirriff's library for receiving sony codes found at www.arcfn.com (got a lot of inspiration from his library).

 

 

#define TOPBIT 0x80000000
int timertx=0; int resettx=0; int bitstx=0; float elapsed=0; unsigned long datatx=0; boolean spacetx = false;

 

void setup(){

  //set PWM: FastPWM, OC2A as top, OC2B sets at bottom, clears on compare
  //COM2B1=1 sets PWM active on pin3, COM2B1=0 disables it
  //Serial.begin(9600);
  pinMode(3, OUTPUT);
  pinMode(11, OUTPUT);
  TCCR2A = _BV(WGM21) | _BV(WGM20);
  TCCR2B = _BV(WGM22) | _BV(CS21);
  OCR2A = 49;
  OCR2B = 24;
  delay(500);

}

 

void loop(){

  delay(2500);
  elapsed=micros();
  SonyIR(0xa90, 12);
  elapsed=45-((micros()-elapsed)/1000);
  delay(elapsed);
  SonyIR(0xa90, 12);
  delay(elapsed);
  SonyIR(0xa90, 12);

 

}

 

boolean SonyIR(unsigned long data, int nbits) {

 

  if(bitstx == 0) { //if IDLE then transmit 
    timertx=0; //reset timer
    TIMSK2 |= _BV(TOIE2); //activate interrupt on overflow (TOV flag, triggers at OCR2A)
    resettx=96; //initial header pulse is 2400us long. 2400/25us ticks = 96
    spacetx = false;  
    datatx=data << (32 - (nbits + 1)); //unsigned long is 32 bits. data gets   shifted so that the leftmost (MSB) will be the first of the 32.
    TCCR2A |= _BV(COM2B1); // Enable pin 3 PWM output for transmission of header
    bitstx=nbits + 1; //bits left to transmit, including header
    return true;
  }
  else {
    return false;
  }

}

 

ISR(TIMER2_OVF_vect, ISR_NOBLOCK){

  //RESET_TIMER2;

 

  //TRANSMISSION

 

  if(bitstx != 0) {  //if we have got something to transmit

 

    timertx++; 
    if(timertx >= resettx) {  //if reset value is reached

 

      timertx=0;
      if(spacetx){  //it was a space that has just been sent thus a total "set" (bit + space) so..  
        spacetx = false; //we are not going to send a space now  
        bitstx = bitstx - 1;  //we got 1 less bit to send
        datatx <<= 1;  //shift it so MSB becomes 1st digit
        TCCR2A |= _BV(COM2B1);  //activate pin 3 PWM (ONE)
          if((datatx & TOPBIT)&&(bitstx != 0)) {  
            resettx = 48;
          }
          else if(!(datatx & TOPBIT)&&(bitstx != 0)){
            resettx = 24;
          }
          else{
            TCCR2A &= ~(_BV(COM2B1));  //deactivate pin 3 PWM (end of transmission, no more bits to send)
            TIMSK2 &= ~(_BV(TOIE2));   //deactivate interrupts on overflow
          }
      }
      else {  //we sent the bit, now we have to "send" the space
        spacetx = true;  //we are sending a space
        resettx = 24; //600us/25us = 24
        TCCR2A &= ~(_BV(COM2B1));
      }
    }
  }

}

EDIT:

Just wanted to add a quick explanation on how that ISR function works. Basically in the setup function, i've started a timer (timer2 to be precise) which is basically a value that increments very rapidily, starting from 0 of course. Now, when this value reaches the value of OCR2A (check out the setup function), an overflow occurs and the timing value goes back to zero, and so on until it reaches OCR2A again. Now, by setting TIMSK2 |= _BV(TOIE2) in the SonyIR function i'm basically setting to 1 the TOIE2 bit in the TIMSK2 register, and if you check the manual, this corresponds to activating the ISR (or interrupt service routine) on overflow. This means that when an overflow occurs (aka timing value reaches OCR2A in our case), the ISR function is called. Basically an ISR is a function that occurs every "x" microsecond/millisecond, depending on how you set OCR2A and other stuff (like prescaling factors).

In the above code, the ISR takes place every 25 microseconds. 

Looks wicked, Tuna. Please

Looks wicked, Tuna. Please tag it Arduino, IR and stuff, so it will be found in Google searches better, help more people :slight_smile:

Oops, forgot to do that,

Oops, forgot to do that, thanks for reminding!

I’m a little fuzzy on how

I’m a little fuzzy on how this works. I understand that the timers run in the background, but in terms of what you do with the timers when it comes to sending a signal out, I don’t quite understand that part or how that could be a background process. Unless you are just sending out a constant repeat signal but even then, you’d need some sort of code to handle the interrupt(which you explain that you are)…am I missing something? I probably am… :slight_smile:

I’ll just sum up how it

I’ll just sum up how it works: 

Basically i have a function called SonyIR that sets everything up, activates interrupts, takes in the data to be transmitted, etc…and MOST importantly of all sets the resettx variable, which is the key of the whole “system”.

Now basically every time the ISR gets exectued, there’s a variable called timer which increments, and the ISR does nothing else UNTIL this timer value equals the reset value. EG: Say i want to send a 2400ms long pulse (the headers) and i know the ISR gets executed every 25ms, then i let the resettx value be = 2400/25 = 96, so the timer will count up to 96. 

Now once 96 or whatever other value is reached, there’s a variable which is spacetx which is boolean, and tells us whether it was a low signal we just sent or a high signal (not to be confused with 1s and 0s in the protocol). Alright so we just sent the header which was 96 “ticks” long, and spacetx is false (cause we sent a high signal), that means that now we gotta sent a space because it is required by the sony protocol. The spacing of a space is 600ms, which in ticks is 24, so again we set the resettx variable to 24, and wait until the timertx variable gets up to that value. Then again, space tx is gonna be true, meaning that now we’ll have to send a high signal, and here starts the real communication (this one was only to send the header).

Data comm. works exactly as above, with the spacetx / resettx trick, only extra thing is the shifting and TOPBIT constant we use to send data, but this is very straight forward. Basically we shift the data so that the bit we have to send matches the position of the 1 in the topbit constant which is: B 10000000000000000000000000000000. Knowing the total bits of the data to be transmitted this becomes quite easy. Now we just AND (bitwise and) the topbit and the data (now shifted to match the the topbit) so that we get out a 1 or 0 bit from it, and we just send it with the above mecahnism.

Hope it makes things a bit (:D) more clear! But keep on asking if there’s something you want to know.

Small update: Just wanted to

Small update: Just wanted to tell you guys that i have made the tone command ( not exactly the tone() function, but the result is the same) work with the above code, using the same timer. As before, there are no delays or no data missing when receiving IR and using tones.